diff options
431 files changed, 21562 insertions, 9799 deletions
diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..72e8ffc0d --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +* diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bc6c4936..d584e41c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -27,6 +27,9 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +if (IOS) + INCLUDE(CmakeLists_IOS.txt) +endif() cmake_minimum_required(VERSION 2.8.7) @@ -77,9 +80,10 @@ if (ARM_TEST STREQUAL "arm") endif() endif() -if (ARM_ID STREQUAL "aarch64") +if (ARM_ID STREQUAL "aarch64" OR ARM_ID STREQUAL "arm64" OR ARM_ID STREQUAL "armv8-a") set(ARM 1) set(ARM8 1) + set(ARCH "armv8-a") endif() if(WIN32 OR ARM) @@ -88,6 +92,14 @@ else() set(OPT_FLAGS_RELEASE "-Ofast") endif() +# BUILD_TAG is used to select the build type to check for a new version +if(BUILD_TAG) + message(STATUS "Building build tag ${BUILD_TAG}") + add_definitions("-DBUILD_TAG=${BUILD_TAG}") +else() + message(STATUS "Building without build tag") +endif() + set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG ${OPT_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${OPT_FLAGS_RELEASE}") @@ -194,6 +206,7 @@ endif() if (BUILD_SHARED_LIBS) message(STATUS "Building internal libraries with position independent code") set(PIC_FLAG "-fPIC") + add_definitions("-DBUILD_SHARED_LIBS") else() message(STATUS "Building internal libraries as static") endif() @@ -219,8 +232,8 @@ if(STATIC) endif() endif() -# default database: -# should be lmdb for testing, memory for production still +# Set default blockchain storage location: +# memory was the default in Cryptonote before Monero implimented LMDB, it still works but is unneccessary. # set(DATABASE memory) set(DATABASE lmdb) @@ -302,6 +315,23 @@ else() message(STATUS "Stack trace on exception disabled") endif() +# Handle OpenSSL, used for sha256sum on binary updates +if (APPLE AND NOT IOS) + if (NOT OpenSSL_DIR) + EXECUTE_PROCESS(COMMAND brew --prefix openssl + OUTPUT_VARIABLE OPENSSL_ROOT_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using OpenSSL found at ${OPENSSL_ROOT_DIR}") + endif() +endif() + +find_package(OpenSSL REQUIRED) +if(STATIC AND NOT IOS) + if(UNIX) + set(OPENSSL_LIBRARIES "${OPENSSL_LIBRARIES};${CMAKE_DL_LIBS}") + endif() +endif() + if (UNIX AND NOT APPLE) # Note that at the time of this writing the -Wstrict-prototypes flag added below will make this fail set(THREADS_PREFER_PTHREAD_FLAG ON) @@ -311,7 +341,7 @@ endif() add_subdirectory(external) # Final setup for miniupnpc -if(UPNP_STATIC) +if(UPNP_STATIC OR IOS) add_definitions("-DUPNP_STATIC") else() add_definitions("-DUPNP_DYNAMIC") @@ -322,8 +352,9 @@ endif() include_directories(${UNBOUND_INCLUDE}) link_directories(${UNBOUND_LIBRARY_DIRS}) -# Final setup for rapidjson -include_directories(external/rapidjson) +# Final setup for easylogging++ +include_directories(${EASYLOGGING_INCLUDE}) +link_directories(${EASYLOGGING_LIBRARY_DIRS}) # Final setup for liblmdb include_directories(${LMDB_INCLUDE}) @@ -514,8 +545,9 @@ else() endif(ARM) - if(ANDROID AND NOT BUILD_GUI_DEPS STREQUAL "ON") + if(ANDROID AND NOT BUILD_GUI_DEPS STREQUAL "ON" OR IOS) #From Android 5: "only position independent executables (PIE) are supported" + message(STATUS "Enabling PIE executable") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIE") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_CXX_FLAGS} -fPIE -pie") @@ -533,7 +565,7 @@ else() endif() if(NOT DEFINED USE_LTO_DEFAULT) - set(USE_LTO_DEFAULT true) + set(USE_LTO_DEFAULT false) endif() set(USE_LTO ${USE_LTO_DEFAULT} CACHE BOOL "Use Link-Time Optimization (Release mode only)") @@ -607,11 +639,13 @@ endif() include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) if(MINGW) set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi) -elseif(APPLE OR FREEBSD OR OPENBSD OR ANDROID) +elseif(APPLE OR OPENBSD OR ANDROID) set(EXTRA_LIBRARIES "") +elseif(FREEBSD) + set(EXTRA_LIBRARIES execinfo) elseif(DRAGONFLY) find_library(COMPAT compat) - set(EXTRA_LIBRARIES ${COMPAT}) + set(EXTRA_LIBRARIES execinfo ${COMPAT}) elseif(NOT MSVC) find_library(RT rt) set(EXTRA_LIBRARIES ${RT}) @@ -622,7 +656,7 @@ list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) if(ANDROID) set(ATOMIC libatomic.a) endif() -if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND ARCH_WIDTH EQUAL "32") +if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND ARCH_WIDTH EQUAL "32" AND NOT IOS) find_library(ATOMIC atomic) list(APPEND EXTRA_LIBRARIES ${ATOMIC}) endif() diff --git a/CMakeLists_IOS.txt b/CMakeLists_IOS.txt new file mode 100644 index 000000000..4dab94cb3 --- /dev/null +++ b/CMakeLists_IOS.txt @@ -0,0 +1,164 @@ +# Portions Copyright (c) 2017, The Monero Project +# This file is based off of the https://code.google.com/archive/p/ios-cmake/ +# It has been altered for Monero iOS development +# +# +# Options: +# +# IOS_PLATFORM = OS (default) or SIMULATOR or SIMULATOR64 +# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders +# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch. +# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. +# +# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder +# By default this location is automatcially chosen based on the IOS_PLATFORM value above. +# If set manually, it will override the default location and force the user of a particular Developer Platform +# +# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder +# By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value. +# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path. +# If set manually, this will force the use of a specific SDK version + + +# Standard settings +set (CMAKE_SYSTEM_NAME Darwin) +set (CMAKE_SYSTEM_VERSION 1) +set (UNIX True) +set (APPLE True) +set (IOS True) + +# Required as of cmake 2.8.10 +set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE) + +# Determine the cmake host system version so we know where to find the iOS SDKs +find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin) +if (CMAKE_UNAME) + exec_program(uname ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION) + string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}") +endif (CMAKE_UNAME) + +# Force the compilers to gcc for iOS +include (CMakeForceCompiler) +# set (MAKE_C_COMPILER "/usr/bin/gcc Apple") +# set (CMAKE_CXX_COMPILER "/usr/bin/g++ Apple") +set(CMAKE_AR ar CACHE FILEPATH "" FORCE) + +# Skip the platform compiler checks for cross compiling +set (CMAKE_CXX_COMPILER_WORKS TRUE) +set (CMAKE_C_COMPILER_WORKS TRUE) + +# All iOS/Darwin specific settings - some may be redundant +set (CMAKE_SHARED_LIBRARY_PREFIX "lib") +set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") +set (CMAKE_SHARED_MODULE_PREFIX "lib") +set (CMAKE_SHARED_MODULE_SUFFIX ".so") +set (CMAKE_MODULE_EXISTS 1) +set (CMAKE_DL_LIBS "") + +set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +# Hidden visibilty is required for cxx on iOS +set (CMAKE_C_FLAGS_INIT "") +set (CMAKE_CXX_FLAGS_INIT "-fvisibility=hidden -fvisibility-inlines-hidden") + +set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") +set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") + +set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names") +set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names") +set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") + +# Setup iOS platform unless specified manually with IOS_PLATFORM +if (NOT DEFINED IOS_PLATFORM) + set (IOS_PLATFORM "OS") +endif (NOT DEFINED IOS_PLATFORM) +set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform") + +# Setup building for arm64 or not +if (NOT DEFINED BUILD_ARM64) + set (BUILD_ARM64 true) +endif (NOT DEFINED BUILD_ARM64) +set (BUILD_ARM64 ${BUILD_ARM64} CACHE STRING "Build arm64 arch or not") + +# Check the platform selection and setup for developer root +if (${IOS_PLATFORM} STREQUAL "OS") + set (IOS_PLATFORM_LOCATION "iPhoneOS.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos") +elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR") + set (SIMULATOR true) + set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") +elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR64") + set (SIMULATOR true) + set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") + + # This causes the installers to properly locate the output libraries + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") +else (${IOS_PLATFORM} STREQUAL "OS") + message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR") +endif (${IOS_PLATFORM} STREQUAL "OS") + +# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT +# Note Xcode 4.3 changed the installation location, choose the most recent one available +exec_program(/usr/bin/xcode-select ARGS -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR) +set (XCODE_POST_43_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${IOS_PLATFORM_LOCATION}/Developer") +set (XCODE_PRE_43_ROOT "/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer") +if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) + if (EXISTS ${XCODE_POST_43_ROOT}) + set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_POST_43_ROOT}) + elseif(EXISTS ${XCODE_PRE_43_ROOT}) + set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_PRE_43_ROOT}) + endif (EXISTS ${XCODE_POST_43_ROOT}) +endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) +set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform") + +# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT +if (NOT DEFINED CMAKE_IOS_SDK_ROOT) + file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*") + if (_CMAKE_IOS_SDKS) + list (SORT _CMAKE_IOS_SDKS) + list (REVERSE _CMAKE_IOS_SDKS) + list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT) + else (_CMAKE_IOS_SDKS) + message (FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.") + endif (_CMAKE_IOS_SDKS) + message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}") +endif (NOT DEFINED CMAKE_IOS_SDK_ROOT) +set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK") + +# Set the sysroot default to the most recent SDK +set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support") + +# set the architecture for iOS +if (NOT DEFINED ARCH) + set (ARCH armv7) +endif() +set (IOS_ARCH ${ARCH}) + +set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string "Build architecture for iOS") + message(STATUS "ios arch: ${IOS_ARCH}") + +# Set the find root to the iOS developer roots and to user defined paths +set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string "iOS find search path root") + +# default to searching for frameworks first +set (CMAKE_FIND_FRAMEWORK FIRST) + +# set up the default search directories for frameworks +set (CMAKE_SYSTEM_FRAMEWORK_PATH + ${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks + ${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks + ${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks +) + +message(STATUS "IOS CMAKE conf finished")
\ No newline at end of file diff --git a/CONTRIBUTING b/CONTRIBUTING.md index 55fcedccd..78d78f7bf 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING.md @@ -1,8 +1,17 @@ -A good way to help is to test, and report bugs. -See http://www.chiark.greenend.org.uk/~sgtatham/bugs.html if you -want to help that way. Testing is invaluable in making a piece +# Contributing to Monero + +A good way to help is to test, and report bugs. See +[How to Report Bugs Effectively (by Simon Tatham)](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) +if you want to help that way. Testing is invaluable in making a piece of software solid and usable. + +## General Guidelines + +* Comments are encouraged. +* If modifying code for which Doxygen headers exist, that header must be modified to match. +* Tests would be nice to have if you're adding functionality. + Patches are preferably to be sent via a github pull request. If that can't be done, patches in "git format-patch" format can be sent (eg, posted to fpaste.org with a long enough timeout and a link @@ -16,15 +25,12 @@ modifying is encourgaged. Proper squashing should be done (eg, if you're making a buggy patch, then a later patch to fix the bug, both patches should be merged). +## Commits and Pull Requests + Commit messages should be sensible. That means a subject line that describes the patch, with an optional longer body that gives details, documentation, etc. -Comments are encouraged. - -If modifying code for which Doxygen headers exist, that header must -be modified to match. - When submitting a pull request on github, make sure your branch is rebased. No merge commits nor stray commits from other people in your submitted branch, please. You may be asked to rebase if there @@ -32,5 +38,3 @@ are conflicts (even trivially resolvable ones). PGP signing commits is strongly encouraged. That should explain why the previous paragraph is here. - -Tests would be nice to have if you're adding functionality. diff --git a/Dockerfile b/Dockerfile index 8a40e9533..297789a8d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,40 +1,36 @@ -FROM debian:testing -MAINTAINER eiabea <developer@eiabea.com> - -# Install clone dependencies -RUN set -e && \ - apt-get update -q && \ - apt-get install -q -y --no-install-recommends ca-certificates git && \ - git clone https://github.com/monero-project/monero.git src && \ - apt-get purge -y git && \ - apt-get clean -q -y && \ - apt-get autoclean -q -y && \ - apt-get autoremove -q -y - -WORKDIR /src - -# Install make dependencies -RUN set -e && \ - apt-get update -q && \ - apt-get install -q -y --no-install-recommends build-essential ca-certificates g++ gcc cmake \ - pkg-config libunbound2 libevent-2.0-5 libgtest-dev libboost-all-dev libdb5.3++-dev libdb5.3-dev libssl1.0-dev && \ - make -j 4 && \ - apt-get purge -y g++ gcc cmake pkg-config && \ - apt-get clean -q -y && \ - apt-get autoclean -q -y && \ - apt-get autoremove -q -y && \ - mkdir /monero && \ - mv /src/build/release/bin/* /monero && \ - rm -rf /src - -WORKDIR /monero +FROM ubuntu:16.04 + +ENV SRC_DIR /usr/local/src/monero + +RUN set -x \ + && buildDeps=' \ + ca-certificates \ + cmake \ + g++ \ + git \ + libboost1.58-all-dev \ + libssl-dev \ + make \ + pkg-config \ + ' \ + && apt-get -qq update \ + && apt-get -qq --no-install-recommends install $buildDeps + +RUN git clone https://github.com/monero-project/monero.git $SRC_DIR +WORKDIR $SRC_DIR +RUN make -j$(nproc) release-static + +RUN cp build/release/bin/* /usr/local/bin/ \ + \ + && rm -r $SRC_DIR \ + && apt-get -qq --auto-remove purge $buildDeps # Contains the blockchain VOLUME /root/.bitmonero # Generate your wallet via accessing the container and run: # cd /wallet -# /./bitmonero/monero-wallet-cli +# monero-wallet-cli VOLUME /wallet ENV LOG_LEVEL 0 @@ -46,4 +42,4 @@ ENV RPC_BIND_PORT 18081 EXPOSE 18080 EXPOSE 18081 -CMD ./monerod --log-level=$LOG_LEVEL --p2p-bind-ip=$P2P_BIND_IP --p2p-bind-port=$P2P_BIND_PORT --rpc-bind-ip=$RPC_BIND_IP --rpc-bind-port=$RPC_BIND_PORT +CMD monerod --log-level=$LOG_LEVEL --p2p-bind-ip=$P2P_BIND_IP --p2p-bind-port=$P2P_BIND_PORT --rpc-bind-ip=$RPC_BIND_IP --rpc-bind-port=$RPC_BIND_PORT @@ -1,4 +1,4 @@ -Copyright (c) 2014-2016, The Monero Project +Copyright (c) 2014-2017, The Monero Project All rights reserved. @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -58,43 +58,55 @@ release-all: mkdir -p build/release cd build/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) +release-static: + mkdir -p build/release + cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + coverage: mkdir -p build/debug cd build/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug -D COVERAGE=ON ../.. && $(MAKE) && $(MAKE) test -release-static-armv6: +# Targets for specific prebuilt builds which will be advertised for updates by their build tag + +release-static-linux-armv6: mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv6zk" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv6zk" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv6" ../.. && $(MAKE) -release-static-armv7: +release-static-linux-armv7: mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv7" ../.. && $(MAKE) release-static-android: mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON ../.. && $(MAKE) + cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" ../.. && $(MAKE) -release-static-armv8: +release-static-linux-armv8: mkdir -p build/release - cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + cd build/release && cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-armv8" ../.. && $(MAKE) -release-static: release-static-64 +release-static-linux-x86_64: + mkdir -p build/release + cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x64" ../.. && $(MAKE) -release-static-64: +release-static-freebsd-x86_64: mkdir -p build/release - cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="freebsd-x64" ../.. && $(MAKE) + +release-static-mac-x86_64: + mkdir -p build/release + cd build/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="mac-x64" ../.. && $(MAKE) -release-static-32: +release-static-linux-i686: mkdir -p build/release - cd build/release && cmake -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release ../.. && $(MAKE) + cd build/release && cmake -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="linux-x86" ../.. && $(MAKE) release-static-win64: mkdir -p build/release - cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=../../cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 ../.. && $(MAKE) + cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=../../cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys64 ../.. && $(MAKE) release-static-win32: mkdir -p build/release - cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 ../.. && $(MAKE) + cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x32" -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 ../.. && $(MAKE) clean: @echo "WARNING: Back-up your wallet if it exists within ./build!" ; \ @@ -1,6 +1,6 @@ # Monero -Copyright (c) 2014-2016, The Monero Project +Copyright (c) 2014-2017, The Monero Project ## Development Resources @@ -21,7 +21,8 @@ Copyright (c) 2014-2016, The Monero Project | OSX 10.10 | amd64 | [![OSX 10.10 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.10)](https://build.getmonero.org/builders/monero-static-osx-10.10) | OSX 10.11 | amd64 | [![OSX 10.11 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.11)](https://build.getmonero.org/builders/monero-static-osx-10.11) | OSX 10.12 | amd64 | [![OSX 10.12 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.12)](https://build.getmonero.org/builders/monero-static-osx-10.12) -| FreeBSD 10.3 | amd64 | [![FreeBSD 10.3 amd64](https://build.getmonero.org/png?builder=monero-static-freebsd64)](https://build.getmonero.org/builders/monero-static-freebsd64) +| FreeBSD 11 | amd64 | [![FreeBSD 11 amd64](https://build.getmonero.org/png?builder=monero-static-freebsd64)](https://build.getmonero.org/builders/monero-static-freebsd64) +| DragonFly BSD 4.6 | amd64 | [![DragonFly BSD amd64](https://build.getmonero.org/png?builder=monero-static-dragonflybsd-amd64)](https://build.getmonero.org/builders/monero-static-dragonflybsd-amd64) | Windows (MSYS2/MinGW) | i686 | [![Windows (MSYS2/MinGW) i686](https://build.getmonero.org/png?builder=monero-static-win32)](https://build.getmonero.org/builders/monero-static-win32) | Windows (MSYS2/MinGW) | amd64 | [![Windows (MSYS2/MinGW) amd64](https://build.getmonero.org/png?builder=monero-static-win64)](https://build.getmonero.org/builders/monero-static-win64) @@ -49,7 +50,7 @@ This is the core implementation of Monero. It is open source and completely free As with many development projects, the repository on Github is considered to be the "staging" area for the latest changes. Before changes are merged into that branch on the main repository, they are tested by individual developers in their own branches, submitted as a pull request, and then subsequently tested by contributors who focus on testing and code reviews. That having been said, the repository should be carefully considered before using it in a production environment, unless there is a patch in the repository for a particular show-stopping issue you are experiencing. It is generally a better idea to use a tagged release for stability. -**Anyone is welcome to contribute to Monero's codebase!** If you have a fix or code change, feel free to submit is as a pull request directly to the "master" branch. In cases where the change is relatively small or does not affect other parts of the codebase it may be merged in immediately by any one of the collaborators. On the other hand, if the change is particularly large or complex, it is expected that it will be discussed at length either well in advance of the pull request being submitted, or even directly on the pull request. +**Anyone is welcome to contribute to Monero's codebase!** If you have a fix or code change, feel free to submit it as a pull request directly to the "master" branch. In cases where the change is relatively small or does not affect other parts of the codebase it may be merged in immediately by any one of the collaborators. On the other hand, if the change is particularly large or complex, it is expected that it will be discussed at length either well in advance of the pull request being submitted, or even directly on the pull request. ## Supporting the Project @@ -79,25 +80,34 @@ There are also several mining pools that kindly donate a portion of their fees, See [LICENSE](LICENSE). -## Monero software updates and consensus protocol changes (hard forking) +# Contributing + +If you want to help out, see [CONTRIBUTING](CONTRIBUTING.md) for a set of guidelines. + +## Vulnerability Response Process + +See [Vulnerability Response Process](VULNERABILITY_RESPONSE_PROCESS.md). -Monero uses a hardforking mechanism to implement new features which requires that -users of Monero software run current versions and update their software on a -regular schedule. Here is the current schedule, versions, and compatibility. -Dates are provided in the format YYYYMMDD. +## Monero software updates and consensus protocol changes (hard fork schedule) +Monero uses a fixed-schedule hard fork mechanism to implement new features. This means that users of Monero (end users and service providers) need to run current versions and update their software on a regular schedule. Here is the current schedule, versions, and compatibility. +Dates are provided in the format YYYY-MM-DD. -| Date | Consensus version | Minimum Monero Version | Recommended Monero Version | Details | + +| Fork Date | Consensus version | Minimum Monero Version | Recommended Monero Version | Details | | ----------------- | ----------------- | ---------------------- | -------------------------- | ------------------ | | 2016-09-21 | v3 | v0.9.4 | v0.10.0 | Splits coinbase into denominations | -| 2017-01-05 | v4 | v0.10.1 | v0.10.1 | Allow normal and RingCT transactions | -| 2017-09-21 | v5 | v0.10.1 | v0.10.1 | Allow only RingCT transactions | +| 2017-01-05 | v4 | v0.10.1 | v0.10.2.1 | Allow normal and RingCT transactions | +| 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm | +| 2017-09-21 | v6 | Not determined as of 2017-03-27 | Not determined as of 2017-03-27 | Allow only RingCT transactions | ## Installing Monero from a Package Packages are available for -* Arch Linux via AUR: [`bitmonero-git`](https://aur.archlinux.org/packages/bitmonero-git) +* Arch Linux (via [AUR](https://aur.archlinux.org/)): + - Stable release: [`monero`](https://aur.archlinux.org/packages/monero) + - Bleeding edge: [`bitmonero-git`](https://aur.archlinux.org/packages/bitmonero-git) * OS X via [Homebrew](http://brew.sh) @@ -135,7 +145,7 @@ library archives (`.a`). | CMake | 3.0.0 | NO | `cmake` | `cmake` | NO | | | pkg-config | any | NO | `pkg-config` | `base-devel` | NO | | | Boost | 1.58 | NO | `libboost-all-dev` | `boost` | NO | C++ libraries | -| libevent | 2.0 | NO | `libevent-dev` | `libevent` | NO | Network IO | +| OpenSSL | basically any | NO | `libssl-dev` | `openssl` | NO | sha256 sum | | libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | NO | DNS resolver | | libminiupnpc | 2.0 | YES | `libminiupnpc-dev` | `miniupnpc` | YES | NAT punching | | libunwind | any | NO | `libunwind8-dev` | `libunwind` | YES | Stack traces | @@ -143,7 +153,6 @@ library archives (`.a`). | ldns | 1.6.17 | NO | `libldns-dev` | `ldns` | YES | SSL toolkit | | expat | 1.1 | NO | `libexpat1-dev` | `expat` | YES | XML parsing | | GTest | 1.5 | YES | `libgtest-dev`^ | `gtest` | YES | Test suite | -| BerkeleyDB | 4.8 | NO | `libdb{,++}-dev` | `db` | YES | Historic DB | | Doxygen | any | NO | `doxygen` | `doxygen` | YES | Documentation | | Graphviz | any | NO | `graphviz` | `graphviz` | YES | Documentation | @@ -288,7 +297,7 @@ application. ### On FreeBSD: -The project can be built from scratch by following instructions for Linux above. +The project can be built from scratch by following instructions for Linux above. If you are running monero in a jail you need to add the flag: `allow.sysvipc=1` to your jail configuration, otherwise lmdb will throw the error message: `Failed to open lmdb environment: Function not implemented`. We expect to add Monero into the ports tree in the near future, which will aid in managing installations using ports or packages. @@ -377,10 +386,6 @@ While monerod and monero-wallet-cli do not use readline directly, most of the fu Note: rlwrap will save things like your seed and private keys, if you supply them on prompt. You may want to not use rlwrap when you use simplewallet to restore from seed, etc. -# Contributing - -If you want to help out, see CONTRIBUTING for a set of guidelines. - # Debugging This section contains general instructions for debugging failed installs or problems encountered with Monero. First ensure you are running the latest version built from the github repo. diff --git a/VULNERABILITY_RESPONSE_PROCESS.md b/VULNERABILITY_RESPONSE_PROCESS.md new file mode 100644 index 000000000..eea3a06e7 --- /dev/null +++ b/VULNERABILITY_RESPONSE_PROCESS.md @@ -0,0 +1,143 @@ +# Monero Vulnerability Response Process + +## Preamble + +Researchers/Hackers: while you research/hack, we ask that you please refrain from committing the following: +- Denial of Service / Active exploiting against the network +- Social Engineering of Monero staff or contractors +- Any physical or electronic attempts against Monero community property and/or data centers + +## I. Point of Contacts for Security Issues + +``` +ric@getmonero.org +BDA6 BD70 42B7 21C4 67A9 759D 7455 C5E3 C0CD CEB9 + +luigi1111@getmonero.org +8777 AB8F 778E E894 87A2 F8E7 F4AC A018 3641 E010 + +moneromooo.monero@gmail.com +48B0 8161 FBDA DFE3 93AD FC3E 686F 0745 4D6C EFC3 +``` + +## II. Security Response Team + +- fluffypony +- luigi1111 +- moneromooo + +## III. Incident Response + +1. Researcher submits report via one or both of two methods: + - a. Email + - b. [HackerOne](https://hackerone.com/monero) + +2. Response Team designates a Response Manager who is in charge of the particular report based on availability and/or knowledge-set + +3. In no more than 3 working days, Response Team should gratefully respond to researcher using only encrypted, secure channels + +4. Response Manager makes inquiries to satisfy any needed information to confirm if submission is indeed a vulnerability + - a. If submission proves to be vulnerable, proceed to next step + - b. If not vulnerable: + - i. Response Manager responds with reasons why submission is not a vulnerability + - ii. Response Manager moves discussion to a new or existing ticket on GitHub if necessary + +5. If over email, Response Manager opens a HackerOne issue for new submission + +6. Establish severity of vulnerability: + - a. HIGH: impacts network as a whole, has potential to break entire network, results in the loss of monero, or is on a scale of great catastrophe + - b. MEDIUM: impacts individual nodes, wallets, or must be carefully exploited + - c. LOW: is not easily exploitable + +7. Respond according to the severity of the vulnerability: + - a. HIGH severities must be notified on website and reddit /r/Monero within 3 working days of classification + - i. The notification should list appropriate steps for users to take, if any + - ii. The notification must not include any details that could suggest an exploitation path + - iii. The latter takes precedence over the former + - b. MEDIUM and HIGH severities will require a Point Release + - c. LOW severities will be addressed in the next Regular Release + +8. Response Team applies appropriate patch(es) + - a. Response Manager designates a PRIVATE git "hotfix branch" to work in + - b. Patches are reviewed with the researcher + - c. Any messages associated with PUBLIC commits during the time of review should not make reference to the security nature of the PRIVATE branch or its commits + - d. Vulnerability announcement is drafted + - i. Include the severity of the vulnerability + - ii. Include all vulnerable systems/apps/code + - iii. Include solutions (if any) if patch cannot be applied + - e. Release date is discussed + +9. At release date, Response Team coordinates with developers to finalize update: + - a. Response Manager propagates the "hotfix branch" to trunk + - b. Response Manager includes vulnerability announcement draft in release notes + - c. Proceed with the Point or Regular Release + +## IV. Post-release Disclosure Process + +1. Response Team has 90 days to fulfill all points within section III + +2. If the Incident Response process in section III is successfully completed: + - a. Response Manager contacts researcher and asks if researcher wishes for credit + - b. Finalize vulnerability announcement draft and include the following: + - i. Project name and URL + - ii. Versions known to be affected + - iii. Versions known to be not affected (for example, the vulnerable code was introduced in a recent version, and older versions are therefore unaffected) + - iv. Versions not checked + - v. Type of vulnerability and its impact + - vi. If already obtained or applicable, a CVE-ID + - vii. The planned, coordinated release date + - viii. Mitigating factors (for example, the vulnerability is only exposed in uncommon, non-default configurations) + - ix. Workarounds (configuration changes users can make to reduce their exposure to the vulnerability) + - x. If applicable, credits to the original reporter + - c. Release finalized vulnerability announcement on website and reddit /r/Monero + - d. For HIGH severities, release finalized vulnerability announcement on well-known mailing lists: + - i. oss-security@lists.openwall.com + - ii. bugtraq@securityfocus.com + - e. If applicable, developers request a CVE-ID + - i. The commit that applied the fix is made reference too in a future commit and includes a CVE-ID + +3. If the Incident Response process in section III is *not* successfully completed: + - a. Response Team and developers organize an IRC meeting to discuss why/what points in section III were not resolved and how the team can resolve them in the future + - b. Any developer meetings immediately following the incident should include points made in section V + - c. If disputes arise about whether or when to disclose information about a vulnerability, the Response Team will publicly discuss the issue via IRC and attempt to reach consensus + - d. If consensus on a timely disclosure is not met (no later than 90 days), the researcher (after 90 days) has every right to expose the vulnerability to the public + +## V. Incident Analysis + +1. Isolate codebase + - a. Response Team and developers should coordinate to work on the following: + - i. Problematic implementation of classes/libraries/functions, etc. + - ii. Focus on apps/distro packaging, etc. + - iii. Operator/config error, etc. + +2. Auditing + - a. Response Team and developers should coordinate to work on the following: + - i. Auditing of problem area(s) as discussed in point 1 + - ii. Generate internal reports and store for future reference + - iii. If results are not sensitive, share with the public via IRC or GitHub + +3. Response Team has 45 days following completion of section III to ensure completion of section V + +## VI. Resolutions + +Any further questions or resolutions regarding the incident(s) between the researcher and response + development team after public disclosure can be addressed via the following: + +- [GitHub](https://github.com/monero-project/monero/issues/) +- [HackerOne](https://hackerone.com/monero) +- [Reddit /r/Monero](https://reddit.com/r/Monero/) +- IRC +- Email + +## VII. Continuous Improvement + +1. Response Team and developers should hold annual meetings to review the previous year's incidents + +2. Response Team or designated person(s) should give a brief presentation, including: + - a. Areas of Monero affected by the incidents + - b. Any network downtime or monetary cost (if any) of the incidents + - c. Ways in which the incidents could have been avoided (if any) + - d. How effective this process was in dealing with the incidents + +3. After the presentation, Response Team and developers should discuss: + - a. Potential changes to development processes to reduce future incidents + - b. Potential changes to this process to improve future responses diff --git a/cmake/32-bit-toolchain.cmake b/cmake/32-bit-toolchain.cmake index 908431697..2f5b87402 100644 --- a/cmake/32-bit-toolchain.cmake +++ b/cmake/32-bit-toolchain.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/cmake/64-bit-toolchain.cmake b/cmake/64-bit-toolchain.cmake index 4fc024c23..0334aef73 100644 --- a/cmake/64-bit-toolchain.cmake +++ b/cmake/64-bit-toolchain.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/cmake/FindUnbound.cmake b/cmake/FindUnbound.cmake index 7e1ad19c6..2e8b444fb 100644 --- a/cmake/FindUnbound.cmake +++ b/cmake/FindUnbound.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, are diff --git a/cmake/test-static-assert.c b/cmake/test-static-assert.c index 0c6dfb151..2ae34757a 100644 --- a/cmake/test-static-assert.c +++ b/cmake/test-static-assert.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/cmake/test-static-assert.cpp b/cmake/test-static-assert.cpp index 0c6dfb151..2ae34757a 100644 --- a/cmake/test-static-assert.cpp +++ b/cmake/test-static-assert.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 6c2ee73ca..7bd411aed 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/contrib/epee/CMakeLists.txt b/contrib/epee/CMakeLists.txt index d466b87e0..e7ff6001b 100644 --- a/contrib/epee/CMakeLists.txt +++ b/contrib/epee/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/contrib/epee/include/hex.h b/contrib/epee/include/hex.h new file mode 100644 index 000000000..f8d6be048 --- /dev/null +++ b/contrib/epee/include/hex.h @@ -0,0 +1,65 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <array> +#include <cstdint> +#include <iosfwd> +#include <string> + +#include "span.h" + +namespace epee +{ + struct to_hex + { + //! \return A std::string containing hex of `src`. + static std::string string(const span<const std::uint8_t> src); + + //! \return An array containing hex of `src`. + template<std::size_t N> + static std::array<char, N * 2> array(const std::array<std::uint8_t, N>& src) noexcept + { + std::array<char, N * 2> out{{}}; + static_assert(N <= 128, "keep the stack size down"); + buffer_unchecked(out.data(), {src.data(), src.size()}); + return out; + } + + //! Append `src` as hex to `out`. + static void buffer(std::ostream& out, const span<const std::uint8_t> src); + + //! Append `< + src + >` as hex to `out`. + static void formatted(std::ostream& out, const span<const std::uint8_t> src); + + private: + //! Write `src` bytes as hex to `out`. `out` must be twice the length + static void buffer_unchecked(char* out, const span<const std::uint8_t> src) noexcept; + }; +} diff --git a/contrib/epee/include/md5_l.inl b/contrib/epee/include/md5_l.inl index 2aa881092..8e339e006 100644 --- a/contrib/epee/include/md5_l.inl +++ b/contrib/epee/include/md5_l.inl @@ -58,14 +58,11 @@ These notices must be retained in any copies of any part of this documentation and/or software. */ -/* do i need all of this just for htonl()? damn. */ -//#include <sys/types.h> -//#include <sys/param.h> -//#include <sys/socket.h> -//#include <netinet/in.h> - - - +#ifdef _WIN32 +# include <winsock2.h> +#else +# include <arpa/inet.h> +#endif #include "md5global.h" #include "md5_l.h" #include "hmac-md5.h" diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index fbfd6941f..6eeb1441f 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -49,12 +49,6 @@ #include <boost/filesystem.hpp> #include <boost/algorithm/string.hpp> -#define ELPP_THREAD_SAFE -#define ELPP_DEFAULT_LOG_FILE "" -#define ELPP_STACKTRACE_ON_CRASH 0 -#define ELPP_DISABLE_DEFAULT_CRASH_HANDLING -#define ELPP_FEATURE_CRASH_LOG 1 -#define ELPP_DISABLE_CHECK_MACROS #include "easylogging++.h" #define MONERO_DEFAULT_LOG_CATEGORY "default" @@ -115,7 +109,7 @@ #define _warn(x) MWARNING(x) #define _erro(x) MERROR(x) -#define MLOG_SET_THREAD_NAME(x) el::Loggers::setThreadName(x) +#define MLOG_SET_THREAD_NAME(x) el::Helpers::setThreadName(x) #ifndef LOCAL_ASSERT #include <assert.h> diff --git a/contrib/epee/include/net/http_auth.h b/contrib/epee/include/net/http_auth.h index f43a19457..bf368e6f4 100644 --- a/contrib/epee/include/net/http_auth.h +++ b/contrib/epee/include/net/http_auth.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h index d8e31521f..e5aa06cb4 100644 --- a/contrib/epee/include/net/http_base.h +++ b/contrib/epee/include/net/http_base.h @@ -29,6 +29,9 @@ #pragma once #include <boost/lexical_cast.hpp> #include <boost/regex.hpp> +#include <boost/utility/string_ref.hpp> +#include <string> +#include <utility> #include "string_tools.h" @@ -90,6 +93,15 @@ namespace net_utils return std::string(); } + static inline void add_field(std::string& out, const boost::string_ref name, const boost::string_ref value) + { + out.append(name.data(), name.size()).append(": "); + out.append(value.data(), value.size()).append("\r\n"); + } + static inline void add_field(std::string& out, const std::pair<std::string, std::string>& field) + { + add_field(out, field.first, field.second); + } struct http_header_info diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index c5aeb9251..67e63f7bf 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -30,6 +30,8 @@ #include <boost/shared_ptr.hpp> #include <boost/regex.hpp> #include <boost/lexical_cast.hpp> +#include <boost/optional/optional.hpp> +#include <boost/utility/string_ref.hpp> //#include <mbstring.h> #include <algorithm> #include <cctype> @@ -45,6 +47,7 @@ #include "string_tools.h" #include "reg_exp_definer.h" #include "http_base.h" +#include "http_auth.h" #include "to_nonconst_iterator.h" #include "net_parse_helpers.h" @@ -104,14 +107,14 @@ using namespace std; //--------------------------------------------------------------------------- static inline const char* get_hex_vals() { - static char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + static const char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; return hexVals; } static inline const char* get_unsave_chars() { //static char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&"; - static char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&"; + static const char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&"; return unsave_chars; } @@ -236,9 +239,6 @@ using namespace std; class http_simple_client: public i_target_handler { - public: - - private: enum reciev_machine_state { @@ -263,6 +263,7 @@ using namespace std; blocked_mode_client m_net_client; std::string m_host_buff; std::string m_port; + http_client_auth m_auth; std::string m_header_cache; http_response_info m_response_info; size_t m_len_in_summary; @@ -280,6 +281,7 @@ using namespace std; , m_net_client() , m_host_buff() , m_port() + , m_auth() , m_header_cache() , m_response_info() , m_len_in_summary(0) @@ -291,21 +293,22 @@ using namespace std; , m_lock() {} - bool set_server(const std::string& address) + bool set_server(const std::string& address, boost::optional<login> user) { http::url_content parsed{}; const bool r = parse_url(address, parsed); CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address); - set_server(std::move(parsed.host), std::to_string(parsed.port)); + set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user)); return true; } - void set_server(std::string host, std::string port) + void set_server(std::string host, std::string port, boost::optional<login> user) { CRITICAL_REGION_LOCAL(m_lock); disconnect(); m_host_buff = std::move(host); m_port = std::move(port); + m_auth = user ? http_client_auth{std::move(*user)} : http_client_auth{}; } bool connect(std::chrono::milliseconds timeout) @@ -334,15 +337,20 @@ using namespace std; return true; } //--------------------------------------------------------------------------- + virtual bool on_header(const http_response_info &headers) + { + return true; + } + //--------------------------------------------------------------------------- inline - bool invoke_get(const std::string& uri, std::chrono::milliseconds timeout, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) + bool invoke_get(const boost::string_ref uri, std::chrono::milliseconds timeout, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) { CRITICAL_REGION_LOCAL(m_lock); return invoke(uri, "GET", body, timeout, ppresponse_info, additional_params); } //--------------------------------------------------------------------------- - inline bool invoke(const std::string& uri, const std::string& method, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) + inline bool invoke(const boost::string_ref uri, const boost::string_ref method, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) { CRITICAL_REGION_LOCAL(m_lock); if(!is_connected()) @@ -354,32 +362,64 @@ using namespace std; return false; } } - m_response_info.clear(); - std::string req_buff = method + " "; - req_buff += uri + " HTTP/1.1\r\n" + - "Host: "+ m_host_buff +"\r\n" + "Content-Length: " + boost::lexical_cast<std::string>(body.size()) + "\r\n"; + std::string req_buff{}; + req_buff.reserve(2048); + req_buff.append(method.data(), method.size()).append(" ").append(uri.data(), uri.size()).append(" HTTP/1.1\r\n"); + add_field(req_buff, "Host", m_host_buff); + add_field(req_buff, "Content-Length", std::to_string(body.size())); //handle "additional_params" - for(fields_list::const_iterator it = additional_params.begin(); it!=additional_params.end(); it++) - req_buff += it->first + ": " + it->second + "\r\n"; - req_buff += "\r\n"; - //-- - - bool res = m_net_client.send(req_buff, timeout); - CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); - if(body.size()) - res = m_net_client.send(body, timeout); - CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); - - if(ppresponse_info) - *ppresponse_info = &m_response_info; - - m_state = reciev_machine_state_header; - return handle_reciev(timeout); + for(const auto& field : additional_params) + add_field(req_buff, field); + + for (unsigned sends = 0; sends < 2; ++sends) + { + const std::size_t initial_size = req_buff.size(); + const auto auth = m_auth.get_auth_field(method, uri); + if (auth) + add_field(req_buff, *auth); + + req_buff += "\r\n"; + //-- + + bool res = m_net_client.send(req_buff, timeout); + CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); + if(body.size()) + res = m_net_client.send(body, timeout); + CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); + + + m_response_info.clear(); + m_state = reciev_machine_state_header; + if (!handle_reciev(timeout)) + return false; + if (m_response_info.m_response_code != 401) + { + if(ppresponse_info) + *ppresponse_info = std::addressof(m_response_info); + return true; + } + + switch (m_auth.handle_401(m_response_info)) + { + case http_client_auth::kSuccess: + break; + case http_client_auth::kBadPassword: + sends = 2; + break; + default: + case http_client_auth::kParseFailure: + LOG_ERROR("Bad server response for authentication"); + return false; + } + req_buff.resize(initial_size); // rollback for new auth generation + } + LOG_ERROR("Client has incorrect username/password for server requiring authentication"); + return false; } //--------------------------------------------------------------------------- - inline bool invoke_post(const std::string& uri, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) + inline bool invoke_post(const boost::string_ref uri, const std::string& body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) { CRITICAL_REGION_LOCAL(m_lock); return invoke(uri, "POST", body, timeout, ppresponse_info, additional_params); @@ -470,6 +510,12 @@ using namespace std; m_header_cache.erase(m_header_cache.begin()+pos+4, m_header_cache.end()); analize_cached_header_and_invoke_state(); + if (!on_header(m_response_info)) + { + MDEBUG("Connection cancelled by on_header"); + m_state = reciev_machine_state_done; + return false; + } m_header_cache.clear(); if(!recv_buff.size() && (m_state != reciev_machine_state_error && m_state != reciev_machine_state_done)) need_more_data = true; @@ -492,7 +538,11 @@ using namespace std; } CHECK_AND_ASSERT_MES(m_len_in_remain >= recv_buff.size(), false, "m_len_in_remain >= recv_buff.size()"); m_len_in_remain -= recv_buff.size(); - m_pcontent_encoding_handler->update_in(recv_buff); + if (!m_pcontent_encoding_handler->update_in(recv_buff)) + { + m_state = reciev_machine_state_done; + return false; + } if(m_len_in_remain == 0) m_state = reciev_machine_state_done; @@ -665,7 +715,11 @@ using namespace std; m_len_in_remain = 0; } - m_pcontent_encoding_handler->update_in(chunk_body); + if (!m_pcontent_encoding_handler->update_in(chunk_body)) + { + m_state = reciev_machine_state_error; + return false; + } if(!m_len_in_remain) m_chunked_state = http_chunked_state_chunk_head; @@ -730,7 +784,7 @@ using namespace std; else if(result[i++].matched)//"User-Agent" body_info.m_user_agent = result[field_val]; else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) - {;} + body_info.m_etc_fields.emplace_back(result[11], result[field_val]); else {CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<<m_cache_to_process);} diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h index c2e44c536..babe49ad7 100644 --- a/contrib/epee/include/net/http_protocol_handler.h +++ b/contrib/epee/include/net/http_protocol_handler.h @@ -54,7 +54,6 @@ namespace net_utils struct http_server_config { std::string m_folder; - std::string m_required_user_agent; boost::optional<login> m_user; critical_section m_lock; }; diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index 5bfaf4767..d9eca2479 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -390,13 +390,6 @@ namespace net_utils return false; } - if (!m_config.m_required_user_agent.empty() && m_query_info.m_header_info.m_user_agent != m_config.m_required_user_agent) - { - LOG_ERROR("simple_http_connection_handler<t_connection_context>::analize_cached_request_header_and_invoke_state(): unexpected user agent: " << m_query_info.m_header_info.m_user_agent); - m_state = http_state_error; - return false; - } - m_cache.erase(0, pos); std::string req_command_str = m_query_info.m_full_request_str; diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index f2a580167..acecbb2d4 100644 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -56,7 +56,7 @@ namespace epee {} bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0", - std::string user_agent = "", boost::optional<net_utils::http::login> user = boost::none) + boost::optional<net_utils::http::login> user = boost::none) { //set self as callback handler @@ -65,11 +65,9 @@ namespace epee //here set folder for hosting reqests m_net_server.get_config_object().m_folder = ""; - // workaround till we get auth/encryption - m_net_server.get_config_object().m_required_user_agent = std::move(user_agent); m_net_server.get_config_object().m_user = std::move(user); - LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); + MGINFO("Binding on " << bind_ip << ":" << bind_port); bool res = m_net_server.init_server(bind_port, bind_ip); if(!res) { diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index cc6987e8f..891089be6 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -25,6 +25,7 @@ // #pragma once +#include <boost/asio/deadline_timer.hpp> #include <boost/uuid/uuid_generators.hpp> #include <boost/unordered_map.hpp> #include <boost/interprocess/detail/atomic.hpp> diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index cfa51e10d..4334029f7 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -29,6 +29,7 @@ #ifndef _NET_UTILS_BASE_H_ #define _NET_UTILS_BASE_H_ +#include <boost/asio/io_service.hpp> #include <boost/uuid/uuid.hpp> #include "string_tools.h" #include "misc_log_ex.h" @@ -159,11 +160,11 @@ inline MAKE_LOGGABLE(connection_context_base, ct, os) #define LOG_TRACE_CC(ct, message) MTRACE(ct << message) #define LOG_CC(level, ct, message) MLOG(level, ct << message) -#define LOG_PRINT_CC_L0(ct, message) LOG_PRINT_L0(epee::net_utils::print_connection_context_short(ct) << message) -#define LOG_PRINT_CC_L1(ct, message) LOG_PRINT_L1(epee::net_utils::print_connection_context_short(ct) << message) -#define LOG_PRINT_CC_L2(ct, message) LOG_PRINT_L2(epee::net_utils::print_connection_context_short(ct) << message) -#define LOG_PRINT_CC_L3(ct, message) LOG_PRINT_L3(epee::net_utils::print_connection_context_short(ct) << message) -#define LOG_PRINT_CC_L4(ct, message) LOG_PRINT_L4(epee::net_utils::print_connection_context_short(ct) << message) +#define LOG_PRINT_CC_L0(ct, message) LOG_PRINT_L0(ct << message) +#define LOG_PRINT_CC_L1(ct, message) LOG_PRINT_L1(ct << message) +#define LOG_PRINT_CC_L2(ct, message) LOG_PRINT_L2(ct << message) +#define LOG_PRINT_CC_L3(ct, message) LOG_PRINT_L3(ct << message) +#define LOG_PRINT_CC_L4(ct, message) LOG_PRINT_L4(ct << message) #define LOG_PRINT_CCONTEXT_L0(message) LOG_PRINT_CC_L0(context, message) #define LOG_PRINT_CCONTEXT_L1(message) LOG_PRINT_CC_L1(context, message) diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h new file mode 100644 index 000000000..ea4ba63dd --- /dev/null +++ b/contrib/epee/include/span.h @@ -0,0 +1,130 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <cstdint> +#include <memory> +#include <type_traits> + +namespace epee +{ + /*! + \brief Non-owning sequence of data. Does not deep copy + + Inspired by `gsl::span` and/or `boost::iterator_range`. This class is + intended to be used as a parameter type for functions that need to take a + writable or read-only sequence of data. Most common cases are `span<char>` + and `span<std::uint8_t>`. Using as a class member is only recommended if + clearly documented as not doing a deep-copy. C-arrays are easily convertible + to this type. + + \note Conversion from C string literal to `span<const char>` will include + the NULL-terminator. + \note Never allows derived-to-base pointer conversion; an array of derived + types is not an array of base types. + */ + template<typename T> + class span + { + /* Supporting class types is tricky - the {ptr,len} constructor will allow + derived-to-base conversions. This is NOT desireable because an array of + derived types is not an array of base types. It is possible to handle + this case, implement when/if needed. */ + static_assert(!std::is_class<T>(), "no class types are currently allowed"); + public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using iterator = pointer; + using const_iterator = const_pointer; + + constexpr span() noexcept : ptr(nullptr), len(0) {} + constexpr span(std::nullptr_t) noexcept : span() {} + + constexpr span(T* const src_ptr, const std::size_t count) noexcept + : ptr(src_ptr), len(count) {} + + //! Conversion from C-array. Prevents common bugs with sizeof + arrays. + template<std::size_t N> + constexpr span(T (&src)[N]) noexcept : span(src, N) {} + + constexpr span(const span&) noexcept = default; + span& operator=(const span&) noexcept = default; + + constexpr iterator begin() const noexcept { return ptr; } + constexpr const_iterator cbegin() const noexcept { return ptr; } + + constexpr iterator end() const noexcept { return begin() + size(); } + constexpr const_iterator cend() const noexcept { return cbegin() + size(); } + + constexpr bool empty() const noexcept { return size() == 0; } + constexpr pointer data() const noexcept { return ptr; } + constexpr std::size_t size() const noexcept { return len; } + constexpr std::size_t size_bytes() const noexcept { return size() * sizeof(value_type); } + + private: + T* ptr; + std::size_t len; + }; + + //! \return `span<const T::value_type>` from a STL compatible `src`. + template<typename T> + constexpr span<const typename T::value_type> to_span(const T& src) + { + // compiler provides diagnostic if size() is not size_t. + return {src.data(), src.size()}; + } + + template<typename T> + constexpr bool has_padding() noexcept + { + return !std::is_pod<T>() || alignof(T) != 1; + } + + //! \return Cast data from `src` as `span<const std::uint8_t>`. + template<typename T> + span<const std::uint8_t> to_byte_span(const span<const T> src) noexcept + { + static_assert(!has_padding<T>(), "source type may have padding"); + return {reinterpret_cast<const std::uint8_t*>(src.data()), src.size_bytes()}; + } + + //! \return `span<const std::uint8_t>` which represents the bytes at `&src`. + template<typename T> + span<const std::uint8_t> as_byte_span(const T& src) noexcept + { + static_assert(!std::is_empty<T>(), "empty types will not work -> sizeof == 1"); + static_assert(!has_padding<T>(), "source type may have padding"); + return {reinterpret_cast<const std::uint8_t*>(std::addressof(src)), sizeof(T)}; + } +} diff --git a/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h index 36177c7e0..823ce6731 100644 --- a/contrib/epee/include/storages/http_abstract_invoke.h +++ b/contrib/epee/include/storages/http_abstract_invoke.h @@ -26,6 +26,9 @@ // #pragma once +#include <boost/utility/string_ref.hpp> +#include <chrono> +#include <string> #include "portable_storage_template_helper.h" #include "net/http_base.h" #include "net/http_server_handlers_map2.h" @@ -35,7 +38,7 @@ namespace epee namespace net_utils { template<class t_request, class t_response, class t_transport> - bool invoke_http_json(const std::string& uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(5), const std::string& method = "GET") + bool invoke_http_json(const boost::string_ref uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref method = "GET") { std::string req_param; if(!serialization::store_t_to_json(out_struct, req_param)) @@ -66,7 +69,7 @@ namespace epee template<class t_request, class t_response, class t_transport> - bool invoke_http_bin(const std::string& uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(5), const std::string& method = "GET") + bool invoke_http_bin(const boost::string_ref uri, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref method = "GET") { std::string req_param; if(!serialization::store_t_to_binary(out_struct, req_param)) @@ -95,7 +98,7 @@ namespace epee } template<class t_request, class t_response, class t_transport> - bool invoke_http_json_rpc(const std::string& uri, std::string method_name, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(5), const std::string& http_method = "GET", const std::string& req_id = "0") + bool invoke_http_json_rpc(const boost::string_ref uri, std::string method_name, const t_request& out_struct, t_response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET", const std::string& req_id = "0") { epee::json_rpc::request<t_request> req_t = AUTO_VAL_INIT(req_t); req_t.jsonrpc = "2.0"; @@ -117,7 +120,7 @@ namespace epee } template<class t_command, class t_transport> - bool invoke_http_json_rpc(const std::string& uri, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(5), const std::string& http_method = "GET", const std::string& req_id = "0") + bool invoke_http_json_rpc(const boost::string_ref uri, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET", const std::string& req_id = "0") { return invoke_http_json_rpc(uri, t_command::methodname(), out_struct, result_struct, transport, timeout, http_method, req_id); } diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 6292e471c..67984b9e4 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -29,18 +29,24 @@ #ifndef _STRING_TOOLS_H_ #define _STRING_TOOLS_H_ +// Previously pulled in by ASIO, further cleanup still required ... +#ifdef _WIN32 +# include <winsock2.h> +# include <windows.h> +#endif + //#include <objbase.h> #include <locale> #include <cstdlib> #include <iomanip> +#include <map> #include <type_traits> -//#include <strsafe.h> #include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid_io.hpp> #include <boost/lexical_cast.hpp> -#include <boost/asio.hpp> -#include <boost/algorithm/string/compare.hpp> #include <boost/algorithm/string.hpp> +#include "hex.h" +#include "span.h" #include "warnings.h" @@ -110,33 +116,10 @@ namespace string_tools return false; } } - //---------------------------------------------------------------------------- - template<class CharT> - std::basic_string<CharT> buff_to_hex(const std::basic_string<CharT>& s) - { - using namespace std; - basic_stringstream<CharT> hexStream; - hexStream << hex << noshowbase << setw(2); - - for(typename std::basic_string<CharT>::const_iterator it = s.begin(); it != s.end(); it++) - { - hexStream << "0x"<< static_cast<unsigned int>(static_cast<unsigned char>(*it)) << " "; - } - return hexStream.str(); - } //---------------------------------------------------------------------------- - template<class CharT> - std::basic_string<CharT> buff_to_hex_nodelimer(const std::basic_string<CharT>& s) + inline std::string buff_to_hex_nodelimer(const std::string& src) { - using namespace std; - basic_stringstream<CharT> hexStream; - hexStream << hex << noshowbase; - - for(typename std::basic_string<CharT>::const_iterator it = s.begin(); it != s.end(); it++) - { - hexStream << setw(2) << setfill('0') << static_cast<unsigned int>(static_cast<unsigned char>(*it)); - } - return hexStream.str(); + return to_hex::string(to_byte_span(to_span(src))); } //---------------------------------------------------------------------------- template<class CharT> @@ -327,26 +310,9 @@ POP_WARNINGS } //---------------------------------------------------------------------------- -//#ifdef _WINSOCK2API_ - inline std::string get_ip_string_from_int32(uint32_t ip) - { - in_addr adr; - adr.s_addr = ip; - const char* pbuf = inet_ntoa(adr); - if(pbuf) - return pbuf; - else - return "[failed]"; - } + std::string get_ip_string_from_int32(uint32_t ip); //---------------------------------------------------------------------------- - inline bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str) - { - ip = inet_addr(ip_str.c_str()); - if(INADDR_NONE == ip) - return false; - - return true; - } + bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str); //---------------------------------------------------------------------------- inline bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres) { @@ -376,7 +342,6 @@ POP_WARNINGS return true; } -//#endif //---------------------------------------------------------------------------- template<typename t> inline std::string get_t_as_hex_nwidth(const t& v, std::streamsize w = 8) @@ -573,9 +538,7 @@ POP_WARNINGS std::string pod_to_hex(const t_pod_type& s) { static_assert(std::is_pod<t_pod_type>::value, "expected pod type"); - std::string buff; - buff.assign(reinterpret_cast<const char*>(&s), sizeof(s)); - return buff_to_hex_nodelimer(buff); + return to_hex::string(as_byte_span(s)); } //---------------------------------------------------------------------------- template<class t_pod_type> diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index 93c9a94f7..1d5fa0394 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -26,7 +26,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -add_library(epee STATIC http_auth.cpp mlog.cpp) +add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp string_tools.cpp) # Build and install libepee if we're building for GUI if (BUILD_GUI_DEPS) if(IOS) @@ -36,4 +36,12 @@ if (BUILD_GUI_DEPS) endif() install(TARGETS epee ARCHIVE DESTINATION ${lib_folder}) -endif()
\ No newline at end of file +endif() + +target_link_libraries(epee + PUBLIC + crypto + easylogging + ${Boost_FILESYSTEM_LIBRARY} + PRIVATE + ${EXTRA_LIBRARIES}) diff --git a/contrib/epee/src/hex.cpp b/contrib/epee/src/hex.cpp new file mode 100644 index 000000000..cfbd3cf87 --- /dev/null +++ b/contrib/epee/src/hex.cpp @@ -0,0 +1,82 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "hex.h" + +#include <iterator> +#include <limits> +#include <ostream> +#include <stdexcept> + +namespace epee +{ + namespace + { + template<typename T> + void write_hex(T&& out, const span<const std::uint8_t> src) + { + static constexpr const char hex[] = u8"0123456789abcdef"; + static_assert(sizeof(hex) == 17, "bad string size"); + for (const std::uint8_t byte : src) + { + *out = hex[byte >> 4]; + ++out; + *out = hex[byte & 0x0F]; + ++out; + } + } + } + + std::string to_hex::string(const span<const std::uint8_t> src) + { + if (std::numeric_limits<std::size_t>::max() / 2 < src.size()) + throw std::range_error("hex_view::to_string exceeded maximum size"); + + std::string out{}; + out.resize(src.size() * 2); + buffer_unchecked(std::addressof(out[0]), src); + return out; + } + + void to_hex::buffer(std::ostream& out, const span<const std::uint8_t> src) + { + write_hex(std::ostreambuf_iterator<char>{out}, src); + } + + void to_hex::formatted(std::ostream& out, const span<const std::uint8_t> src) + { + out.put('<'); + buffer(out, src); + out.put('>'); + } + + void to_hex::buffer_unchecked(char* out, const span<const std::uint8_t> src) noexcept + { + return write_hex(out, src); + } +} diff --git a/contrib/epee/src/http_auth.cpp b/contrib/epee/src/http_auth.cpp index 7fd32dabc..30e562700 100644 --- a/contrib/epee/src/http_auth.cpp +++ b/contrib/epee/src/http_auth.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -67,6 +67,7 @@ #include <type_traits> #include "crypto/crypto.h" +#include "hex.h" #include "md5_l.h" #include "string_coding.h" @@ -104,25 +105,6 @@ namespace //// Digest Algorithms - template<std::size_t N> - std::array<char, N * 2> to_hex(const std::array<std::uint8_t, N>& digest) noexcept - { - static constexpr const char alphabet[] = u8"0123456789abcdef"; - static_assert(sizeof(alphabet) == 17, "bad alphabet size"); - - // TODO upgrade (improve performance) of to hex in epee string tools - std::array<char, N * 2> out{{}}; - auto current = out.begin(); - for (const std::uint8_t byte : digest) - { - *current = alphabet[byte >> 4]; - ++current; - *current = alphabet[byte & 0x0F]; - ++current; - } - return out; - } - struct md5_ { static constexpr const boost::string_ref name = ceref(u8"MD5"); @@ -156,7 +138,7 @@ namespace std::array<std::uint8_t, 16> digest{{}}; md5::MD5Final(digest.data(), std::addressof(ctx)); - return to_hex(digest); + return epee::to_hex::array(digest); } }; constexpr const boost::string_ref md5_::name; @@ -362,7 +344,7 @@ namespace server_parameters best{}; - const std::list<field>& fields = response.m_additional_fields; + const std::list<field>& fields = response.m_header_info.m_etc_fields; auto current = fields.begin(); const auto end = fields.end(); while (true) diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index 7f9ef4b3a..7487fdbd2 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -33,8 +33,13 @@ INITIALIZE_EASYLOGGINGPP +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "logging" + #define MLOG_BASE_FORMAT "%datetime{%Y-%M-%d %H:%m:%s.%g}\t%thread\t%level\t%logger\t%loc\t%msg" +#define MLOG_LOG(x) CINFO(el::base::Writer,el::base::DispatchAction::FileOnlyLog,MONERO_DEFAULT_LOG_CATEGORY) << x + using namespace epee; static std::string generate_log_filename(const char *base) @@ -88,10 +93,10 @@ static const char *get_default_categories(int level) switch (level) { case 0: - categories = "*:WARNING,net*:FATAL,global:INFO,verify:FATAL,stacktrace:INFO"; + categories = "*:WARNING,net:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,stacktrace:INFO,logging:INFO"; break; case 1: - categories = "*:WARNING,global:INFO,stacktrace:INFO"; + categories = "*:WARNING,global:INFO,stacktrace:INFO,logging:INFO"; break; case 2: categories = "*:DEBUG"; @@ -142,7 +147,7 @@ void mlog_configure(const std::string &filename_base, bool console) void mlog_set_categories(const char *categories) { el::Loggers::setCategories(categories); - MINFO("Mew log categories: " << categories); + MLOG_LOG("New log categories: " << categories); } // maps epee style log level to new logging system @@ -150,7 +155,7 @@ void mlog_set_log_level(int level) { const char *categories = get_default_categories(level); el::Loggers::setCategories(categories); - MINFO("Mew log categories: " << categories); + MLOG_LOG("New log categories: " << categories); } void mlog_set_log(const char *log) diff --git a/contrib/epee/src/string_tools.cpp b/contrib/epee/src/string_tools.cpp new file mode 100644 index 000000000..d04b16b75 --- /dev/null +++ b/contrib/epee/src/string_tools.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "string_tools.h" + +#ifdef _WIN32 +# include <winsock2.h> +#else +# include <arpa/inet.h> +# include <netinet/in.h> +#endif + +namespace epee +{ +namespace string_tools +{ + std::string get_ip_string_from_int32(uint32_t ip) + { + in_addr adr; + adr.s_addr = ip; + const char* pbuf = inet_ntoa(adr); + if(pbuf) + return pbuf; + else + return "[failed]"; + } + //---------------------------------------------------------------------------- + bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str) + { + ip = inet_addr(ip_str.c_str()); + if(INADDR_NONE == ip) + return false; + + return true; + } +} +} + diff --git a/contrib/snap/daemon.bash b/contrib/snap/daemon.bash deleted file mode 100755 index 41d1275de..000000000 --- a/contrib/snap/daemon.bash +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -e - -export LD_LIBRARY_PATH=${SNAP_LIBRARY_PATH}:${SNAP}/usr/lib/x86_64-linux-gnu -export HOME=${SNAP_DATA} -cd ${SNAP_DATA} - -ARGS= -if [ -e "${SNAP_DATA}/etc/monerod.conf" ]; then - ARGS="--config-file ${SNAP_DATA}/etc/monerod.conf" -fi - -exec ${SNAP}/bin/monerod --detach $ARGS diff --git a/contrib/snap/log.bash b/contrib/snap/log.bash deleted file mode 100755 index c81efeb2c..000000000 --- a/contrib/snap/log.bash +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -e - -exec tail -c +0 -F ${SNAP_DATA}/.bitmonero/bitmonero.log diff --git a/contrib/snap/monerod-wrapper b/contrib/snap/monerod-wrapper new file mode 100755 index 000000000..f7266e11c --- /dev/null +++ b/contrib/snap/monerod-wrapper @@ -0,0 +1,8 @@ +#!/bin/sh + +if [ ! -d "$SNAP_USER_DATA/etc" ]; then + mkdir $SNAP_USER_DATA/etc/ + cp -R $SNAP/etc/monerod.conf $SNAP_USER_DATA/etc/monerod.conf +fi + +exec "$SNAP/bin/monerod" "$@" diff --git a/contrib/snap/monerod.conf b/contrib/snap/monerod.conf new file mode 100644 index 000000000..9b3d308ed --- /dev/null +++ b/contrib/snap/monerod.conf @@ -0,0 +1,9 @@ +# Configuration for monerod +# Syntax: any command line option may be specified as 'clioptionname=value'. +# See 'monerod --help' for all available options. + +# Overrided by snap: +# data-dir=/var/lib/monero +# log-file=/var/log/monero/monero.log + +log-level=0 diff --git a/contrib/snap/setup/gui/icon.png b/contrib/snap/setup/gui/icon.png Binary files differnew file mode 100644 index 000000000..17b8bd47b --- /dev/null +++ b/contrib/snap/setup/gui/icon.png diff --git a/snapcraft.yaml b/contrib/snap/snapcraft.yaml index 17910392b..11d480d10 100644 --- a/snapcraft.yaml +++ b/contrib/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: monero -version: 0.10.1 # Current stable version +version: 0.10.2-1 summary: "Monero: the secure, private, untraceable cryptocurrency https://getmonero.org" description: | Monero is a private, secure, untraceable, decentralised digital currency. @@ -12,20 +12,20 @@ apps: monerod: daemon: forking command: | - monerod --detach --data-dir ${SNAP_DATA} + monerod-wrapper --detach --data-dir ${SNAP_COMMON} --config-file ${SNAP_USER_DATA}/etc/monerod.conf plugs: - network - network-bind monero-wallet-rpc: command: | - monero-wallet-rpc --log-file ${SNAP_USER_DATA} + monero-wallet-rpc --log-file ${SNAP_USER_DATA} plugs: - home - network - network-bind monero-wallet-cli: command: | - monero-wallet-cli --log-file ${SNAP_USER_DATA} + monero-wallet-cli --log-file ${SNAP_USER_DATA} plugs: - home - network @@ -63,3 +63,13 @@ parts: - usr/lib/ - -usr/lib/gcc - -usr/share + + dist-files: + plugin: dump + source: . + organize: + contrib/snap/monerod.conf: etc/monerod.conf + contrib/snap/monerod-wrapper: bin/monerod-wrapper + prime: + - etc + - bin diff --git a/contrib/snap/wallet.bash b/contrib/snap/wallet.bash deleted file mode 100755 index 004f1371e..000000000 --- a/contrib/snap/wallet.bash +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -e - -export LD_LIBRARY_PATH=${SNAP_LIBRARY_PATH}:${SNAP}/usr/lib/x86_64-linux-gnu -export HOME=${SNAP_USER_DATA} -cd ${SNAP_USER_DATA} - -exec ${SNAP}/usr/bin/rlwrap ${SNAP}/bin/monero-wallet-cli "$@" diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 91611b117..93366a4fa 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -34,11 +34,12 @@ # We always compile if we are building statically to reduce static dependency issues... # ...except for FreeBSD, because FreeBSD is a special case that doesn't play well with # others. - -find_package(Miniupnpc QUIET) +if(NOT IOS) + find_package(Miniupnpc QUIET) +endif() # If we have the correct shared version and we're not building static, use it -if(STATIC) +if(STATIC OR IOS) set(USE_SHARED_MINIUPNPC false) elseif(MINIUPNP_FOUND AND MINIUPNPC_VERSION_1_7_OR_HIGHER) set(USE_SHARED_MINIUPNPC true) @@ -98,3 +99,4 @@ else() endif() add_subdirectory(db_drivers) +add_subdirectory(easylogging++) diff --git a/external/db_drivers/CMakeLists.txt b/external/db_drivers/CMakeLists.txt index 5bc7b5c24..b00bd8c22 100644 --- a/external/db_drivers/CMakeLists.txt +++ b/external/db_drivers/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/external/db_drivers/liblmdb/CMakeLists.txt b/external/db_drivers/liblmdb/CMakeLists.txt index 3d55f0eae..d3d3df6ad 100644 --- a/external/db_drivers/liblmdb/CMakeLists.txt +++ b/external/db_drivers/liblmdb/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/external/db_drivers/liblmdb/mdb.c b/external/db_drivers/liblmdb/mdb.c index d778037b0..3552bd2a9 100644 --- a/external/db_drivers/liblmdb/mdb.c +++ b/external/db_drivers/liblmdb/mdb.c @@ -137,7 +137,7 @@ typedef SSIZE_T ssize_t; #include <unistd.h> #endif -#if defined(__sun) || defined(ANDROID) +#if defined(__sun) || defined(__ANDROID__) /* Most platforms have posix_memalign, older may only have memalign */ #define HAVE_MEMALIGN 1 #include <malloc.h> @@ -153,7 +153,7 @@ typedef SSIZE_T ssize_t; # define MDB_USE_SYSV_SEM 1 # endif # define MDB_FDATASYNC fsync -#elif defined(ANDROID) +#elif defined(__ANDROID__) # define MDB_FDATASYNC fsync #endif @@ -298,7 +298,7 @@ union semun { */ #ifndef MDB_USE_ROBUST /* Android currently lacks Robust Mutex support. So does glibc < 2.4. */ -# if defined(MDB_USE_POSIX_MUTEX) && (defined(ANDROID) || \ +# if defined(MDB_USE_POSIX_MUTEX) && (defined(__ANDROID__) || \ (defined(__GLIBC__) && GLIBC_VER < 0x020004)) # define MDB_USE_ROBUST 0 # else @@ -809,6 +809,16 @@ typedef struct MDB_txbody { uint32_t mtb_magic; /** Format of this lock file. Must be set to #MDB_LOCK_FORMAT. */ uint32_t mtb_format; + /** The ID of the last transaction committed to the database. + * This is recorded here only for convenience; the value can always + * be determined by reading the main database meta pages. + */ + volatile txnid_t mtb_txnid; + /** The number of slots that have been used in the reader table. + * This always records the maximum count, it is not decremented + * when readers release their slots. + */ + volatile unsigned mtb_numreaders; #if defined(_WIN32) || defined(MDB_USE_POSIX_SEM) char mtb_rmname[MNAME_LEN]; #elif defined(MDB_USE_SYSV_SEM) @@ -820,16 +830,6 @@ typedef struct MDB_txbody { */ mdb_mutex_t mtb_rmutex; #endif - /** The ID of the last transaction committed to the database. - * This is recorded here only for convenience; the value can always - * be determined by reading the main database meta pages. - */ - volatile txnid_t mtb_txnid; - /** The number of slots that have been used in the reader table. - * This always records the maximum count, it is not decremented - * when readers release their slots. - */ - volatile unsigned mtb_numreaders; } MDB_txbody; /** The actual reader table definition. */ diff --git a/external/easylogging++/CMakeLists.txt b/external/easylogging++/CMakeLists.txt new file mode 100644 index 000000000..d8d061db0 --- /dev/null +++ b/external/easylogging++/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (c) 2014-2017, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +cmake_minimum_required(VERSION 2.8.7) + +project(easylogging CXX) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +find_package(Threads) + +add_library(easylogging + easylogging++.cc) + +include_directories("${CMAKE_CURRENT_SOURCE_DIR}") +include_directories("${CMAKE_CURRENT_BINARY_DIR}") +target_link_libraries(easylogging + PRIVATE + ${CMAKE_THREAD_LIBS_INIT}) + diff --git a/external/easylogging++/ea_config.h b/external/easylogging++/ea_config.h new file mode 100644 index 000000000..2524d3477 --- /dev/null +++ b/external/easylogging++/ea_config.h @@ -0,0 +1,10 @@ +#pragma once + +#define ELPP_THREAD_SAFE +#define ELPP_DEFAULT_LOG_FILE "" +#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__ +#else +#define ELPP_FEATURE_CRASH_LOG 1 +#endif +#define ELPP_DISABLE_DEFAULT_CRASH_HANDLING +#define ELPP_NO_CHECK_MACROS diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc new file mode 100644 index 000000000..efa876664 --- /dev/null +++ b/external/easylogging++/easylogging++.cc @@ -0,0 +1,3098 @@ +// +// Bismillah ar-Rahmaan ar-Raheem +// +// Easylogging++ v9.94.1 +// Cross-platform logging library for C++ applications +// +// Copyright (c) 2017 muflihun.com +// +// This library is released under the MIT Licence. +// http://labs.muflihun.com/easyloggingpp/licence.php +// +// https://github.com/muflihun/easyloggingpp +// https://muflihun.github.io/easyloggingpp +// http://muflihun.com +// + +#include "easylogging++.h" + +#if defined(AUTO_INITIALIZE_EASYLOGGINGPP) +INITIALIZE_EASYLOGGINGPP +#endif + +namespace el { + +// el::base::utils +namespace base { +namespace utils { + +/// @brief Aborts application due with user-defined status +static void abort(int status, const std::string& reason) { + // Both status and reason params are there for debugging with tools like gdb etc + ELPP_UNUSED(status); + ELPP_UNUSED(reason); +#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) + // Ignore msvc critical error dialog - break instead (on debug mode) + _asm int 3 +#else + ::abort(); +#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) +} + +} // namespace utils +} // namespace base + +// el + +// LevelHelper + +const char* LevelHelper::convertToString(Level level) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (level == Level::Global) return "GLOBAL"; + if (level == Level::Debug) return "DEBUG"; + if (level == Level::Info) return "INFO"; + if (level == Level::Warning) return "WARNING"; + if (level == Level::Error) return "ERROR"; + if (level == Level::Fatal) return "FATAL"; + if (level == Level::Verbose) return "VERBOSE"; + if (level == Level::Trace) return "TRACE"; + return "UNKNOWN"; +} + +struct StringToLevelItem { + const char* levelString; + Level level; +}; + +static struct StringToLevelItem stringToLevelMap[] = { + { "global", Level::Global }, + { "debug", Level::Debug }, + { "info", Level::Info }, + { "warning", Level::Warning }, + { "error", Level::Error }, + { "fatal", Level::Fatal }, + { "verbose", Level::Verbose }, + { "trace", Level::Trace } +}; + +Level LevelHelper::convertFromString(const char* levelStr) { + for (auto& item : stringToLevelMap) { + if (base::utils::Str::cStringCaseEq(levelStr, item.levelString)) { + return item.level; + } + } + return Level::Unknown; +} + +Level LevelHelper::convertFromStringPrefix(const char* levelStr) { + if ((strncmp(levelStr, "GLOBAL", 6) == 0) || (strncmp(levelStr, "global", 6) == 0)) + return Level::Global; + if ((strncmp(levelStr, "DEBUG", 5) == 0) || (strncmp(levelStr, "debug", 5) == 0)) + return Level::Debug; + if ((strncmp(levelStr, "INFO", 4) == 0) || (strncmp(levelStr, "info", 4) == 0)) + return Level::Info; + if ((strncmp(levelStr, "WARNING", 7) == 0) || (strncmp(levelStr, "warning", 7) == 0)) + return Level::Warning; + if ((strncmp(levelStr, "ERROR", 5) == 0) || (strncmp(levelStr, "error", 5) == 0)) + return Level::Error; + if ((strncmp(levelStr, "FATAL", 5) == 0) || (strncmp(levelStr, "fatal", 5) == 0)) + return Level::Fatal; + if ((strncmp(levelStr, "VERBOSE", 7) == 0) || (strncmp(levelStr, "verbose", 7) == 0)) + return Level::Verbose; + if ((strncmp(levelStr, "TRACE", 5) == 0) || (strncmp(levelStr, "trace", 5) == 0)) + return Level::Trace; + return Level::Unknown; +} + +void LevelHelper::forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) { + base::type::EnumType lIndexMax = LevelHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); + } while (*startIndex <= lIndexMax); +} + +// ConfigurationTypeHelper + +const char* ConfigurationTypeHelper::convertToString(ConfigurationType configurationType) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (configurationType == ConfigurationType::Enabled) return "ENABLED"; + if (configurationType == ConfigurationType::Filename) return "FILENAME"; + if (configurationType == ConfigurationType::Format) return "FORMAT"; + if (configurationType == ConfigurationType::ToFile) return "TO_FILE"; + if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT"; + if (configurationType == ConfigurationType::SubsecondPrecision) return "SUBSECOND_PRECISION"; + if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING"; + if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE"; + if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD"; + return "UNKNOWN"; +} + +struct ConfigurationStringToTypeItem { + const char* configString; + ConfigurationType configType; +}; + +static struct ConfigurationStringToTypeItem configStringToTypeMap[] = { + { "enabled", ConfigurationType::Enabled }, + { "to_file", ConfigurationType::ToFile }, + { "to_standard_output", ConfigurationType::ToStandardOutput }, + { "format", ConfigurationType::Format }, + { "filename", ConfigurationType::Filename }, + { "subsecond_precision", ConfigurationType::SubsecondPrecision }, + { "milliseconds_width", ConfigurationType::MillisecondsWidth }, + { "performance_tracking", ConfigurationType::PerformanceTracking }, + { "max_log_file_size", ConfigurationType::MaxLogFileSize }, + { "log_flush_threshold", ConfigurationType::LogFlushThreshold }, +}; + +ConfigurationType ConfigurationTypeHelper::convertFromString(const char* configStr) { + for (auto& item : configStringToTypeMap) { + if (base::utils::Str::cStringCaseEq(configStr, item.configString)) { + return item.configType; + } + } + return ConfigurationType::Unknown; +} + +void ConfigurationTypeHelper::forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) { + base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); + } while (*startIndex <= cIndexMax); +} + +// Configuration + +Configuration::Configuration(const Configuration& c) : + m_level(c.m_level), + m_configurationType(c.m_configurationType), + m_value(c.m_value) { +} + +Configuration& Configuration::operator=(const Configuration& c) { + if (&c != this) { + m_level = c.m_level; + m_configurationType = c.m_configurationType; + m_value = c.m_value; + } + return *this; +} + +/// @brief Full constructor used to sets value of configuration +Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string& value) : + m_level(level), + m_configurationType(configurationType), + m_value(value) { +} + +void Configuration::log(el::base::type::ostream_t& os) const { + os << LevelHelper::convertToString(m_level) + << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType) + << ELPP_LITERAL(" = ") << m_value.c_str(); +} + +/// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. +Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType) : + m_level(level), + m_configurationType(configurationType) { +} + +bool Configuration::Predicate::operator()(const Configuration* conf) const { + return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); +} + +// Configurations + +Configurations::Configurations(void) : + m_configurationFile(std::string()), + m_isFromFile(false) { +} + +Configurations::Configurations(const std::string& configurationFile, bool useDefaultsForRemaining, + Configurations* base) : + m_configurationFile(configurationFile), + m_isFromFile(false) { + parseFromFile(configurationFile, base); + if (useDefaultsForRemaining) { + setRemainingToDefault(); + } +} + +bool Configurations::parseFromFile(const std::string& configurationFile, Configurations* base) { + // We initial assertion with true because if we have assertion diabled, we want to pass this + // check and if assertion is enabled we will have values re-assigned any way. + bool assertionPassed = true; + ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)) == true, + "Configuration file [" << configurationFile << "] does not exist!"); + if (!assertionPassed) { + return false; + } + bool success = Parser::parseFromFile(configurationFile, this, base); + m_isFromFile = success; + return success; +} + +bool Configurations::parseFromText(const std::string& configurationsString, Configurations* base) { + bool success = Parser::parseFromText(configurationsString, this, base); + if (success) { + m_isFromFile = false; + } + return success; +} + +void Configurations::setFromBase(Configurations* base) { + if (base == nullptr || base == this) { + return; + } + base::threading::ScopedLock scopedLock(base->lock()); + for (Configuration*& conf : base->list()) { + set(conf); + } +} + +bool Configurations::hasConfiguration(ConfigurationType configurationType) { + base::type::EnumType lIndex = LevelHelper::kMinValid; + bool result = false; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) { + result = true; + } + return result; + }); + return result; +} + +bool Configurations::hasConfiguration(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); +#if ELPP_COMPILER_INTEL + // We cant specify template types here, Intel C++ throws compilation error + // "error: type name is not allowed" + return RegistryWithPred::get(level, configurationType) != nullptr; +#else + return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType) != nullptr; +#endif // ELPP_COMPILER_INTEL +} + +void Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either + } +} + +void Configurations::set(Configuration* conf) { + if (conf == nullptr) { + return; + } + set(conf->level(), conf->configurationType(), conf->value()); +} + +void Configurations::setToDefault(void) { + setGlobally(ConfigurationType::Enabled, std::string("true"), true); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); +#else + ELPP_UNUSED(base::consts::kDefaultLogFile); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) +#if defined(ELPP_NO_LOG_TO_FILE) + setGlobally(ConfigurationType::ToFile, std::string("false"), true); +#else + setGlobally(ConfigurationType::ToFile, std::string("true"), true); +#endif // defined(ELPP_NO_LOG_TO_FILE) + setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); + setGlobally(ConfigurationType::SubsecondPrecision, std::string("3"), true); + setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true); + setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true); + setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true); + + setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true); + set(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); +} + +void Configurations::setRemainingToDefault(void) { + base::threading::ScopedLock scopedLock(lock()); +#if defined(ELPP_NO_LOG_TO_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("false")); +#else + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); +#endif // defined(ELPP_NO_LOG_TO_FILE) +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%func] [%loc] %msg")); +} + +bool Configurations::Parser::parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base) { + sender->setFromBase(base); + std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in); + ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing."); + bool parsedSuccessfully = false; + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (fileStream_.good()) { + std::getline(fileStream_, line); + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; +} + +bool Configurations::Parser::parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base) { + sender->setFromBase(base); + bool parsedSuccessfully = false; + std::stringstream ss(configurationsString); + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (std::getline(ss, line)) { + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; +} + +void Configurations::Parser::ignoreComments(std::string* line) { + std::size_t foundAt = 0; + std::size_t quotesStart = line->find("\""); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = line->find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') { + // Do not erase slash yet - we will erase it in parseLine(..) while loop + quotesEnd = line->find("\"", quotesEnd + 2); + } + } + if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) { + if (foundAt < quotesEnd) { + foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1); + } + *line = line->substr(0, foundAt); + } +} + +bool Configurations::Parser::isLevel(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); +} + +bool Configurations::Parser::isComment(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); +} + +bool Configurations::Parser::isConfig(const std::string& line) { + std::size_t assignment = line.find('='); + return line != "" && + ((line[0] >= 'A' && line[0] <= 'Z') || (line[0] >= 'a' && line[0] <= 'z')) && + (assignment != std::string::npos) && + (line.size() > assignment); +} + +bool Configurations::Parser::parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, + Level* currLevel, + Configurations* conf) { + ConfigurationType currConfig = ConfigurationType::Unknown; + std::string currValue = std::string(); + *line = base::utils::Str::trim(*line); + if (isComment(*line)) return true; + ignoreComments(line); + *line = base::utils::Str::trim(*line); + if (line->empty()) { + // Comment ignored + return true; + } + if (isLevel(*line)) { + if (line->size() <= 2) { + return true; + } + *currLevelStr = line->substr(1, line->size() - 2); + *currLevelStr = base::utils::Str::toUpper(*currLevelStr); + *currLevelStr = base::utils::Str::trim(*currLevelStr); + *currLevel = LevelHelper::convertFromString(currLevelStr->c_str()); + return true; + } + if (isConfig(*line)) { + std::size_t assignment = line->find('='); + *currConfigStr = line->substr(0, assignment); + *currConfigStr = base::utils::Str::toUpper(*currConfigStr); + *currConfigStr = base::utils::Str::trim(*currConfigStr); + currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str()); + currValue = line->substr(assignment + 1); + currValue = base::utils::Str::trim(currValue); + std::size_t quotesStart = currValue.find("\"", 0); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = currValue.find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') { + currValue = currValue.erase(quotesEnd - 1, 1); + quotesEnd = currValue.find("\"", quotesEnd + 2); + } + } + if (quotesStart != std::string::npos && quotesEnd != std::string::npos) { + // Quote provided - check and strip if valid + ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in [" + << currConfigStr << "]"); + ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]"); + if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) { + // Explicit check in case if assertion is disabled + currValue = currValue.substr(quotesStart + 1, quotesEnd - 1); + } + } + } + ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]"); + ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]"); + if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) { + return false; // unrecognizable level or config + } + conf->set(*currLevel, currConfig, currValue); + return true; +} + +void Configurations::unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); + if (conf == nullptr) { + unsafeSet(level, configurationType, value); + } +} + +void Configurations::unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); + if (conf == nullptr) { + registerNew(new Configuration(level, configurationType, value)); + } else { + conf->setValue(value); + } + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); + } +} + +void Configurations::setGlobally(ConfigurationType configurationType, const std::string& value, + bool includeGlobalLevel) { + if (includeGlobalLevel) { + set(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + set(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); +} + +void Configurations::unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, + bool includeGlobalLevel) { + if (includeGlobalLevel) { + unsafeSet(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); +} + +// LogBuilder + +void LogBuilder::convertToColoredOutput(base::type::string_t* logLine, Level level) { + if (!m_termSupportsColor) return; + const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); + if (level == Level::Error || level == Level::Fatal) + *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; + else if (level == Level::Warning) + *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; + else if (level == Level::Debug) + *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor; + else if (level == Level::Info) + *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor; + else if (level == Level::Trace) + *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor; +} + +// Logger + +Logger::Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); +} + +Logger::Logger(const std::string& id, const Configurations& configurations, + base::LogStreamsReferenceMap* logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + configure(configurations); +} + +Logger::Logger(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; +} + +Logger& Logger::operator=(const Logger& logger) { + if (&logger != this) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + } + return *this; +} + +void Logger::configure(const Configurations& configurations) { + m_isConfigured = false; // we set it to false in case if we fail + initUnflushedCount(); + if (m_typedConfigurations != nullptr) { + Configurations* c = const_cast<Configurations*>(m_typedConfigurations->configurations()); + if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { + // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE + flush(); + } + } + base::threading::ScopedLock scopedLock(lock()); + if (m_configurations != configurations) { + m_configurations.setFromBase(const_cast<Configurations*>(&configurations)); + } + base::utils::safeDelete(m_typedConfigurations); + m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); + resolveLoggerFormatSpec(); + m_isConfigured = true; +} + +void Logger::reconfigure(void) { + ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); + configure(m_configurations); +} + +bool Logger::isValidId(const std::string& id) { + for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { + if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { + return false; + } + } + return true; +} + +void Logger::flush(void) { + ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); + base::threading::ScopedLock scopedLock(lock()); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + flush(LevelHelper::castFromInt(lIndex), nullptr); + return false; + }); +} + +void Logger::flush(Level level, base::type::fstream_t* fs) { + if (fs == nullptr && m_typedConfigurations->toFile(level)) { + fs = m_typedConfigurations->fileStream(level); + } + if (fs != nullptr) { + fs->flush(); + m_unflushedCount.find(level)->second = 0; + } +} + +void Logger::initUnflushedCount(void) { + m_unflushedCount.clear(); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); + return false; + }); +} + +void Logger::resolveLoggerFormatSpec(void) const { + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + base::LogFormat* logFormat = + const_cast<base::LogFormat*>(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); + base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); + return false; + }); +} + +// el::base +namespace base { + +// el::base::utils +namespace utils { + +// File + +base::type::fstream_t* File::newFileStream(const std::string& filename) { + base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), + base::type::fstream_t::out +#if !defined(ELPP_FRESH_LOG_FILE) + | base::type::fstream_t::app +#endif + ); +#if defined(ELPP_UNICODE) + std::locale elppUnicodeLocale(""); +# if ELPP_OS_WINDOWS + std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16<wchar_t>); + elppUnicodeLocale = elppUnicodeLocaleWindows; +# endif // ELPP_OS_WINDOWS + fs->imbue(elppUnicodeLocale); +#endif // defined(ELPP_UNICODE) + if (fs->is_open()) { + fs->flush(); + } else { + base::utils::safeDelete(fs); + ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); + } + return fs; +} + +std::size_t File::getSizeOfFile(base::type::fstream_t* fs) { + if (fs == nullptr) { + return 0; + } + std::streampos currPos = fs->tellg(); + fs->seekg(0, fs->end); + std::size_t size = static_cast<std::size_t>(fs->tellg()); + fs->seekg(currPos); + return size; +} + +bool File::pathExists(const char* path, bool considerFile) { + if (path == nullptr) { + return false; + } +#if ELPP_OS_UNIX + ELPP_UNUSED(considerFile); + struct stat st; + return (stat(path, &st) == 0); +#elif ELPP_OS_WINDOWS + DWORD fileType = GetFileAttributesA(path); + if (fileType == INVALID_FILE_ATTRIBUTES) { + return false; + } + return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); +#endif // ELPP_OS_UNIX +} + +bool File::createPath(const std::string& path) { + if (path.empty()) { + return false; + } + if (base::utils::File::pathExists(path.c_str())) { + return true; + } + int status = -1; + + char* currPath = const_cast<char*>(path.c_str()); + std::string builtPath = std::string(); +#if ELPP_OS_UNIX + if (path[0] == '/') { + builtPath = "/"; + } + currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0); +#elif ELPP_OS_WINDOWS + // Use secure functions API + char* nextTok_ = nullptr; + currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_); + ELPP_UNUSED(nextTok_); +#endif // ELPP_OS_UNIX + while (currPath != nullptr) { + builtPath.append(currPath); + builtPath.append(base::consts::kFilePathSeperator); +#if ELPP_OS_UNIX + status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); + currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0); +#elif ELPP_OS_WINDOWS + status = _mkdir(builtPath.c_str()); + currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_); +#endif // ELPP_OS_UNIX + } + if (status == -1) { + ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); + return false; + } + return true; +} + +std::string File::extractPathFromFilename(const std::string& fullPath, const char* separator) { + if ((fullPath == "") || (fullPath.find(separator) == std::string::npos)) { + return fullPath; + } + std::size_t lastSlashAt = fullPath.find_last_of(separator); + if (lastSlashAt == 0) { + return std::string(separator); + } + return fullPath.substr(0, lastSlashAt + 1); +} + +void File::buildStrippedFilename(const char* filename, char buff[], const std::string &commonPrefix, std::size_t limit) { + if (!commonPrefix.empty()) { + if (!strncmp(filename, commonPrefix.c_str(), commonPrefix.size())) + filename += commonPrefix.size(); + } + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); +} + +void File::buildBaseFilename(const std::string& fullPath, char buff[], std::size_t limit, const char* separator) { + const char *filename = fullPath.c_str(); + std::size_t lastSlashAt = fullPath.find_last_of(separator); + filename += lastSlashAt ? lastSlashAt+1 : 0; + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); +} + +// Str + +bool Str::wildCardMatch(const char* str, const char* pattern) { + while (*pattern) { + switch (*pattern) { + case '?': + if (!*str) + return false; + ++str; + ++pattern; + break; + case '*': + if (wildCardMatch(str, pattern + 1)) + return true; + if (*str && wildCardMatch(str + 1, pattern)) + return true; + return false; + default: + if (*str++ != *pattern++) + return false; + break; + } + } + return !*str && !*pattern; +} + +std::string& Str::ltrim(std::string& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) { + return !std::isspace(c); + } )); + return str; +} + +std::string& Str::rtrim(std::string& str) { + str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) { + return !std::isspace(c); + }).base(), str.end()); + return str; +} + +std::string& Str::trim(std::string& str) { + return ltrim(rtrim(str)); +} + +bool Str::startsWith(const std::string& str, const std::string& start) { + return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); +} + +bool Str::endsWith(const std::string& str, const std::string& end) { + return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); +} + +std::string& Str::replaceAll(std::string& str, char replaceWhat, char replaceWith) { + std::replace(str.begin(), str.end(), replaceWhat, replaceWith); + return str; +} + +std::string& Str::replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith) { + if (replaceWhat == replaceWith) + return str; + std::size_t foundAt = std::string::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { + str.replace(foundAt, replaceWhat.length(), replaceWith); + } + return str; +} + +void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { + str.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + ++foundAt; + } else { + str.replace(foundAt, replaceWhat.length(), replaceWith); + return; + } + } +} +#if defined(ELPP_UNICODE) +void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith) { + replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); +} +#endif // defined(ELPP_UNICODE) + +std::string& Str::toUpper(std::string& str) { + std::transform(str.begin(), str.end(), str.begin(), ::toupper); + return str; +} + +bool Str::cStringEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + return strcmp(s1, s2) == 0; +} + +bool Str::cStringCaseEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + + // With thanks to cygwin for this code + int d = 0; + + while (true) { + const int c1 = toupper(*s1++); + const int c2 = toupper(*s2++); + + if (((d = c1 - c2) != 0) || (c2 == '\0')) { + break; + } + } + + return d == 0; +} + +bool Str::contains(const char* str, char c) { + for (; *str; ++str) { + if (*str == c) + return true; + } + return false; +} + +char* Str::convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded) { + char localBuff[10] = ""; + char* p = localBuff + sizeof(localBuff) - 2; + if (n > 0) { + for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) + *--p = static_cast<char>(n % 10 + '0'); + } else { + *--p = '0'; + --len; + } + if (zeroPadded) + while (p > localBuff && len-- > 0) *--p = static_cast<char>('0'); + return addToBuff(p, buf, bufLim); +} + +char* Str::addToBuff(const char* str, char* buf, const char* bufLim) { + while ((buf < bufLim) && ((*buf = *str++) != '\0')) + ++buf; + return buf; +} + +char* Str::clearBuff(char buff[], std::size_t lim) { + STRCPY(buff, "", lim); + ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro + return buff; +} + +/// @brief Converst wchar* to char* +/// NOTE: Need to free return value after use! +char* Str::wcharPtrToCharPtr(const wchar_t* line) { + std::size_t len_ = wcslen(line) + 1; + char* buff_ = static_cast<char*>(malloc(len_ + 1)); +# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + std::wcstombs(buff_, line, len_); +# elif ELPP_OS_WINDOWS + std::size_t convCount_ = 0; + mbstate_t mbState_; + ::memset(static_cast<void*>(&mbState_), 0, sizeof(mbState_)); + wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); +# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + return buff_; +} + +// OS + +#if ELPP_OS_WINDOWS +/// @brief Gets environment variables for Windows based OS. +/// We are not using <code>getenv(const char*)</code> because of CRT deprecation +/// @param varname Variable name to get environment variable value for +/// @return If variable exist the value of it otherwise nullptr +const char* OS::getWindowsEnvironmentVariable(const char* varname) { + const DWORD bufferLen = 50; + static char buffer[bufferLen]; + if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { + return buffer; + } + return nullptr; +} +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID +std::string OS::getProperty(const char* prop) { + char propVal[PROP_VALUE_MAX + 1]; + int ret = __system_property_get(prop, propVal); + return ret == 0 ? std::string() : std::string(propVal); +} + +std::string OS::getDeviceName(void) { + std::stringstream ss; + std::string manufacturer = getProperty("ro.product.manufacturer"); + std::string model = getProperty("ro.product.model"); + if (manufacturer.empty() || model.empty()) { + return std::string(); + } + ss << manufacturer << "-" << model; + return ss.str(); +} +#endif // ELPP_OS_ANDROID + +const std::string OS::getBashOutput(const char* command) { +#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + if (command == nullptr) { + return std::string(); + } + FILE* proc = nullptr; + if ((proc = popen(command, "r")) == nullptr) { + ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); + return std::string(); + } + char hBuff[4096]; + if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { + pclose(proc); + if (hBuff[strlen(hBuff) - 1] == '\n') { + hBuff[strlen(hBuff) - 1] = '\0'; + } + return std::string(hBuff); + } + return std::string(); +#else + ELPP_UNUSED(command); + return std::string(); +#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) +} + +std::string OS::getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand) { +#if ELPP_OS_UNIX + const char* val = getenv(variableName); +#elif ELPP_OS_WINDOWS + const char* val = getWindowsEnvironmentVariable(variableName); +#endif // ELPP_OS_UNIX + if ((val == nullptr) || ((strcmp(val, "") == 0))) { +#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + // Try harder on unix-based systems + std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); + if (valBash.empty()) { + return std::string(defaultVal); + } else { + return valBash; + } +#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX + ELPP_UNUSED(alternativeBashCommand); + return std::string(defaultVal); +#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + } + return std::string(val); +} + +std::string OS::currentUser(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownUser); + return std::string("android"); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID +} + +std::string OS::currentHost(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownHost); + return getDeviceName(); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID +} + +bool OS::termSupportsColor(void) { + std::string term = getEnvironmentVariable("TERM", ""); + return term == "xterm" || term == "xterm-color" || term == "xterm-256color" + || term == "screen" || term == "linux" || term == "cygwin" + || term == "screen-256color"; +} + +// DateTime + +void DateTime::gettimeofday(struct timeval* tv) { +#if ELPP_OS_WINDOWS + if (tv != nullptr) { +# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const unsigned __int64 delta_ = 11644473600000000Ui64; +# else + const unsigned __int64 delta_ = 11644473600000000ULL; +# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const double secOffSet = 0.000001; + const unsigned long usecOffSet = 1000000; + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + unsigned __int64 present = 0; + present |= fileTime.dwHighDateTime; + present = present << 32; + present |= fileTime.dwLowDateTime; + present /= 10; // mic-sec + // Subtract the difference + present -= delta_; + tv->tv_sec = static_cast<long>(present * secOffSet); + tv->tv_usec = static_cast<long>(present % usecOffSet); + } +#else + ::gettimeofday(tv, nullptr); +#endif // ELPP_OS_WINDOWS +} + +std::string DateTime::getDateTime(const char* format, const base::SubsecondPrecision* ssPrec) { + struct timeval currTime; + gettimeofday(&currTime); + return timevalToString(currTime, format, ssPrec); +} + +std::string DateTime::timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec) { + struct ::tm timeInfo; + buildTimeInfo(&tval, &timeInfo); + const int kBuffSize = 30; + char buff_[kBuffSize] = ""; + parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(tval.tv_usec / ssPrec->m_offset), + ssPrec); + return std::string(buff_); +} + +base::type::string_t DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { + base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit); + const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; + for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { + if (time <= base::consts::kTimeFormats[i].value) { + break; + } + if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) { + break; + } + time /= static_cast<decltype(time)>(base::consts::kTimeFormats[i].value); + unit = base::consts::kTimeFormats[i + 1].unit; + } + base::type::stringstream_t ss; + ss << time << " " << unit; + return ss.str(); +} + +unsigned long long DateTime::getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit) { + if (timestampUnit == base::TimestampUnit::Microsecond) { + return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) - + static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec)); + } + // milliseconds + auto conv = [](const struct timeval& tim) { + return static_cast<unsigned long long>((tim.tv_sec * 1000) + (tim.tv_usec / 1000)); + }; + return static_cast<unsigned long long>(conv(endTime) - conv(startTime)); +} + +struct ::tm* DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { +#if ELPP_OS_UNIX + time_t rawTime = currTime->tv_sec; + ::localtime_r(&rawTime, timeInfo); + return timeInfo; +#else +# if ELPP_COMPILER_MSVC + ELPP_UNUSED(currTime); + time_t t; + _time64(&t); + localtime_s(timeInfo, &t); + return timeInfo; +# else + // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method + time_t rawTime = currTime->tv_sec; + struct tm* tmInf = localtime(&rawTime); + *timeInfo = *tmInf; + return timeInfo; +# endif // ELPP_COMPILER_MSVC +#endif // ELPP_OS_UNIX +} + +char* DateTime::parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec) { + const char* bufLim = buf + bufSz; + for (; *format; ++format) { + if (*format == base::consts::kFormatSpecifierChar) { + switch (*++format) { + case base::consts::kFormatSpecifierChar: // Escape + break; + case '\0': // End + --format; + break; + case 'd': // Day + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); + continue; + case 'a': // Day of week (short) + buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); + continue; + case 'A': // Day of week (long) + buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); + continue; + case 'M': // month + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); + continue; + case 'b': // month (short) + buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); + continue; + case 'B': // month (long) + buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); + continue; + case 'y': // year (two digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); + continue; + case 'Y': // year (four digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); + continue; + case 'h': // hour (12-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); + continue; + case 'H': // hour (24-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); + continue; + case 'm': // minute + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); + continue; + case 's': // second + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); + continue; + case 'z': // subsecond part + case 'g': + buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim); + continue; + case 'F': // AM/PM + buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim); + continue; + default: + continue; + } + } + if (buf == bufLim) break; + *buf++ = *format; + } + return buf; +} + +// CommandLineArgs + +void CommandLineArgs::setArgs(int argc, char** argv) { + m_params.clear(); + m_paramsWithValue.clear(); + if (argc == 0 || argv == nullptr) { + return; + } + m_argc = argc; + m_argv = argv; + for (int i = 1; i < m_argc; ++i) { + const char* v = (strstr(m_argv[i], "=")); + if (v != nullptr && strlen(v) > 0) { + std::string key = std::string(m_argv[i]); + key = key.substr(0, key.find_first_of('=')); + if (hasParamWithValue(key.c_str())) { + ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" + << getParamValue(key.c_str()) << "]"); + } else { + m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1))); + } + } + if (v == nullptr) { + if (hasParam(m_argv[i])) { + ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists"); + } else { + m_params.push_back(std::string(m_argv[i])); + } + } + } +} + +bool CommandLineArgs::hasParamWithValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); +} + +const char* CommandLineArgs::getParamValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey))->second.c_str(); +} + +bool CommandLineArgs::hasParam(const char* paramKey) const { + return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); +} + +bool CommandLineArgs::empty(void) const { + return m_params.empty() && m_paramsWithValue.empty(); +} + +std::size_t CommandLineArgs::size(void) const { + return m_params.size() + m_paramsWithValue.size(); +} + +base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) { + for (int i = 1; i < c.m_argc; ++i) { + os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]"); + if (i < c.m_argc - 1) { + os << ELPP_LITERAL(" "); + } + } + return os; +} + +} // namespace utils + +// el::base::threading +namespace threading { + +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# if ELPP_ASYNC_LOGGING +static void msleep(int ms) { + // Only when async logging enabled - this is because async is strict on compiler +# if defined(ELPP_NO_SLEEP_FOR) + usleep(ms * 1000); +# else + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +# endif // defined(ELPP_NO_SLEEP_FOR) +} +# endif // ELPP_ASYNC_LOGGING +# endif // !ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED + +} // namespace threading + +// el::base + +// SubsecondPrecision + +void SubsecondPrecision::init(int width) { + if (width < 1 || width > 6) { + width = base::consts::kDefaultSubsecondPrecision; + } + m_width = width; + switch (m_width) { + case 3: + m_offset = 1000; + break; + case 4: + m_offset = 100; + break; + case 5: + m_offset = 10; + break; + case 6: + m_offset = 1; + break; + default: + m_offset = 1000; + break; + } +} + +// LogFormat + +LogFormat::LogFormat(void) : + m_level(Level::Unknown), + m_userFormat(base::type::string_t()), + m_format(base::type::string_t()), + m_dateTimeFormat(std::string()), + m_flags(0x0), + m_currentUser(base::utils::OS::currentUser()), + m_currentHost(base::utils::OS::currentHost()) { +} + +LogFormat::LogFormat(Level level, const base::type::string_t& format) + : m_level(level), m_userFormat(format), m_currentUser(base::utils::OS::currentUser()), + m_currentHost(base::utils::OS::currentHost()) { + parseFromFormat(m_userFormat); +} + +LogFormat::LogFormat(const LogFormat& logFormat): + m_level(logFormat.m_level), + m_userFormat(logFormat.m_userFormat), + m_format(logFormat.m_format), + m_dateTimeFormat(logFormat.m_dateTimeFormat), + m_flags(logFormat.m_flags), + m_currentUser(logFormat.m_currentUser), + m_currentHost(logFormat.m_currentHost) { +} + +LogFormat::LogFormat(LogFormat&& logFormat) { + m_level = std::move(logFormat.m_level); + m_userFormat = std::move(logFormat.m_userFormat); + m_format = std::move(logFormat.m_format); + m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat); + m_flags = std::move(logFormat.m_flags); + m_currentUser = std::move(logFormat.m_currentUser); + m_currentHost = std::move(logFormat.m_currentHost); +} + +LogFormat& LogFormat::operator=(const LogFormat& logFormat) { + if (&logFormat != this) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + m_currentUser = logFormat.m_currentUser; + m_currentHost = logFormat.m_currentHost; + } + return *this; +} + +bool LogFormat::operator==(const LogFormat& other) { + return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format && + m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags; +} + +/// @brief Updates format to be used while logging. +/// @param userFormat User provided format +void LogFormat::parseFromFormat(const base::type::string_t& userFormat) { + // We make copy because we will be changing the format + // i.e, removing user provided date format from original format + // and then storing it. + base::type::string_t formatCopy = userFormat; + m_flags = 0x0; + auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { + if (hasFlag(flag)) { + // If we already have flag we remove the escape chars so that '%%' is turned to '%' + // even after specifier resolution - this is because we only replaceFirst specifier + formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + ++foundAt; + } + } else { + if (!hasFlag(flag)) addFlag(flag); + } + } + }; + conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); + conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); + conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); + conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); + conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); + conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); + conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); + conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); + conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); + conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); + conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); + conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); + conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); + conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); + // For date/time we need to extract user's date format first + std::size_t dateIndex = std::string::npos; + if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { + while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); + } + if (dateIndex != std::string::npos) { + addFlag(base::FormatFlags::DateTime); + updateDateFormat(dateIndex, formatCopy); + } + } + m_format = formatCopy; + updateFormatSpec(); +} + +void LogFormat::updateDateFormat(std::size_t index, base::type::string_t& currFormat) { + if (hasFlag(base::FormatFlags::DateTime)) { + index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier); + } + const base::type::char_t* ptr = currFormat.c_str() + index; + if ((currFormat.size() > index) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << static_cast<char>(*ptr); + } + currFormat.erase(index, count); + m_dateTimeFormat = ss.str(); + } else { + // No format provided, use default + if (hasFlag(base::FormatFlags::DateTime)) { + m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat); + } + } +} + +void LogFormat::updateFormatSpec(void) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (m_level == Level::Debug) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kDebugLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kDebugLevelShortLogValue); + } else if (m_level == Level::Info) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kInfoLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kInfoLevelShortLogValue); + } else if (m_level == Level::Warning) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kWarningLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kWarningLevelShortLogValue); + } else if (m_level == Level::Error) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kErrorLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kErrorLevelShortLogValue); + } else if (m_level == Level::Fatal) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kFatalLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kFatalLevelShortLogValue); + } else if (m_level == Level::Verbose) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kVerboseLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kVerboseLevelShortLogValue); + } else if (m_level == Level::Trace) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kTraceLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kTraceLevelShortLogValue); + } + if (hasFlag(base::FormatFlags::User)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, + m_currentUser); + } + if (hasFlag(base::FormatFlags::Host)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, + m_currentHost); + } + // Ignore Level::Global and Level::Unknown +} + +// TypedConfigurations + +TypedConfigurations::TypedConfigurations(Configurations* configurations, + base::LogStreamsReferenceMap* logStreamsReference) { + m_configurations = configurations; + m_logStreamsReference = logStreamsReference; + build(m_configurations); +} + +TypedConfigurations::TypedConfigurations(const TypedConfigurations& other) { + this->m_configurations = other.m_configurations; + this->m_logStreamsReference = other.m_logStreamsReference; + build(m_configurations); +} + +bool TypedConfigurations::enabled(Level level) { + return getConfigByVal<bool>(level, &m_enabledMap, "enabled"); +} + +bool TypedConfigurations::toFile(Level level) { + return getConfigByVal<bool>(level, &m_toFileMap, "toFile"); +} + +const std::string& TypedConfigurations::filename(Level level) { + return getConfigByRef<std::string>(level, &m_filenameMap, "filename"); +} + +bool TypedConfigurations::toStandardOutput(Level level) { + return getConfigByVal<bool>(level, &m_toStandardOutputMap, "toStandardOutput"); +} + +const base::LogFormat& TypedConfigurations::logFormat(Level level) { + return getConfigByRef<base::LogFormat>(level, &m_logFormatMap, "logFormat"); +} + +const base::SubsecondPrecision& TypedConfigurations::subsecondPrecision(Level level) { + return getConfigByRef<base::SubsecondPrecision>(level, &m_subsecondPrecisionMap, "subsecondPrecision"); +} + +const base::MillisecondsWidth& TypedConfigurations::millisecondsWidth(Level level) { + return getConfigByRef<base::MillisecondsWidth>(level, &m_subsecondPrecisionMap, "millisecondsWidth"); +} + +bool TypedConfigurations::performanceTracking(Level level) { + return getConfigByVal<bool>(level, &m_performanceTrackingMap, "performanceTracking"); +} + +base::type::fstream_t* TypedConfigurations::fileStream(Level level) { + return getConfigByRef<base::FileStreamPtr>(level, &m_fileStreamMap, "fileStream").get(); +} + +std::size_t TypedConfigurations::maxLogFileSize(Level level) { + return getConfigByVal<std::size_t>(level, &m_maxLogFileSizeMap, "maxLogFileSize"); +} + +std::size_t TypedConfigurations::logFlushThreshold(Level level) { + return getConfigByVal<std::size_t>(level, &m_logFlushThresholdMap, "logFlushThreshold"); +} + +void TypedConfigurations::build(Configurations* configurations) { + base::threading::ScopedLock scopedLock(lock()); + auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming + base::utils::Str::trim(boolStr); + return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); + }; + setValue(Level::Global, base::FileStreamPtr(NULL), &m_fileStreamMap); + std::vector<Configuration*> withFileSizeLimit; + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + // We cannot use switch on strong enums because Intel C++ dont support them yet + if (conf->configurationType() == ConfigurationType::Enabled) { + setValue(conf->level(), getBool(conf->value()), &m_enabledMap); + } else if (conf->configurationType() == ConfigurationType::ToFile) { + setValue(conf->level(), getBool(conf->value()), &m_toFileMap); + } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { + setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); + } else if (conf->configurationType() == ConfigurationType::Filename) { + // We do not yet configure filename but we will configure in another + // loop. This is because if file cannot be created, we will force ToFile + // to be false. Because configuring logger is not necessarily performance + // sensative operation, we can live with another loop; (by the way this loop + // is not very heavy either) + } else if (conf->configurationType() == ConfigurationType::Format) { + setValue(conf->level(), base::LogFormat(conf->level(), + base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap); + } else if (conf->configurationType() == ConfigurationType::SubsecondPrecision) { + setValue(Level::Global, + base::SubsecondPrecision(static_cast<int>(getULong(conf->value()))), &m_subsecondPrecisionMap); + } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { + setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); + } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { + setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_maxLogFileSizeMap); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + withFileSizeLimit.push_back(conf); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { + setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_logFlushThresholdMap); + } + } + // As mentioned earlier, we will now set filename configuration in separate loop to deal with non-existent files + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + if (conf->configurationType() == ConfigurationType::Filename) { + insertFile(conf->level(), conf->value()); + } + } + for (std::vector<Configuration*>::iterator conf = withFileSizeLimit.begin(); + conf != withFileSizeLimit.end(); ++conf) { + // This is not unsafe as mutex is locked in currect scope + unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); + } +} + +unsigned long TypedConfigurations::getULong(std::string confVal) { + bool valid = true; + base::utils::Str::trim(confVal); + valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(), + [](char c) { + return !base::utils::Str::isDigit(c); + }) == confVal.end(); + if (!valid) { + valid = false; + ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]"); + return 0; + } + return atol(confVal.c_str()); +} + +std::string TypedConfigurations::resolveFilename(const std::string& filename) { + std::string resultingFilename = filename; + std::size_t dateIndex = std::string::npos; + std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename); + if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) { + while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1); + } + if (dateIndex != std::string::npos) { + const char* ptr = resultingFilename.c_str() + dateIndex; + // Goto end of specifier + ptr += dateTimeFormatSpecifierStr.size(); + std::string fmt; + if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count); + fmt = ss.str(); + } else { + fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename); + } + base::SubsecondPrecision ssPrec(3); + std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &ssPrec); + base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename + base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); + } + } + return resultingFilename; +} + +void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) { +#if defined(ELPP_NO_LOG_TO_FILE) + setValue(level, false, &m_toFileMap); + ELPP_UNUSED(fullFilename); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(nullptr))); + return; +#endif + if (fullFilename.empty()) + return; + std::string resolvedFilename = resolveFilename(fullFilename); + if (resolvedFilename.empty()) { + std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" + << LevelHelper::convertToString(level) << "]"; + } + std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator); + if (filePath.size() < resolvedFilename.size()) { + base::utils::File::createPath(filePath); + } + auto create = [&](Level level) { + base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename); + base::type::fstream_t* fs = nullptr; + if (filestreamIter == m_logStreamsReference->end()) { + // We need a completely new stream, nothing to share with + fs = base::utils::File::newFileStream(resolvedFilename); + m_filenameMap.insert(std::make_pair(level, resolvedFilename)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs))); + m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level)))); + } else { + // Woops! we have an existing one, share it! + m_filenameMap.insert(std::make_pair(level, filestreamIter->first)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second))); + fs = filestreamIter->second.get(); + } + if (fs == nullptr) { + // We display bad file error from newFileStream() + ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" + << LevelHelper::convertToString(level) << "] to FALSE", false); + setValue(level, false, &m_toFileMap); + } + }; + // If we dont have file conf for any level, create it for Level::Global first + // otherwise create for specified level + create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level); +} + +bool TypedConfigurations::unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + if (fs == nullptr) { + return true; + } + std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + std::size_t currFileSize = base::utils::File::getSizeOfFile(fs); + if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) { + std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename"); + ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level [" + << LevelHelper::convertToString(level) << "]"); + fs->close(); + preRollOutCallback(fname.c_str(), currFileSize); + fs->open(fname, std::fstream::out | std::fstream::trunc); + return true; + } + return false; +} + +// RegisteredHitCounters + +bool RegisteredHitCounters::validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->validateHitCounts(n); + bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0); + return result; +} + +/// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one +/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned +bool RegisteredHitCounters::validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + // Do not use validateHitCounts here since we do not want to reset counter here + // Note the >= instead of > because we are incrementing + // after this check + if (counter->hitCounts() >= n) + return true; + counter->increment(); + return false; +} + +/// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one +/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned +bool RegisteredHitCounters::validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->increment(); + // Do not use validateHitCounts here since we do not want to reset counter here + if (counter->hitCounts() <= n) + return true; + return false; +} + +// RegisteredLoggers + +RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : + m_defaultLogBuilder(defaultLogBuilder) { + m_defaultConfigurations.setToDefault(); +} + +Logger* RegisteredLoggers::get(const std::string& id, bool forceCreation) { + base::threading::ScopedLock scopedLock(lock()); + Logger* logger_ = base::utils::Registry<Logger, std::string>::get(id); + if (logger_ == nullptr && forceCreation) { + bool validId = Logger::isValidId(id); + if (!validId) { + ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); + return nullptr; + } + logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference); + logger_->m_logBuilder = m_defaultLogBuilder; + registerNew(id, logger_); + LoggerRegistrationCallback* callback = nullptr; + for (const std::pair<std::string, base::type::LoggerRegistrationCallbackPtr>& h + : m_loggerRegistrationCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(logger_); + } + } + } + return logger_; +} + +bool RegisteredLoggers::remove(const std::string& id) { + if (id == base::consts::kDefaultLoggerId) { + return false; + } + Logger* logger = base::utils::Registry<Logger, std::string>::get(id); + if (logger != nullptr) { + unregister(logger); + } + return true; +} + +void RegisteredLoggers::unsafeFlushAll(void) { + ELPP_INTERNAL_INFO(1, "Flushing all log files"); + for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin(); + it != m_logStreamsReference.end(); ++it) { + if (it->second.get() == nullptr) continue; + it->second->flush(); + } +} + +// VRegistry + +VRegistry::VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { +} + +/// @brief Sets verbose level. Accepted range is 0-9 +void VRegistry::setLevel(base::type::VerboseLevel level) { + base::threading::ScopedLock scopedLock(lock()); + if (level > 9) + m_level = base::consts::kMaxVerboseLevel; + else + m_level = level; +} + +void VRegistry::setModules(const char* modules) { + base::threading::ScopedLock scopedLock(lock()); + auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) { + if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev))); + ss.str(std::string("")); + ss << chr; + } + if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx))); + ss.str(std::string("")); + ss << chr; + } + ss << sfx; + }; + auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) { + if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) { + addSuffix(ss, ".h", nullptr); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".c", ".h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cpp", ".c"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cc", ".cpp"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cxx", ".cc"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".-inl.h", ".cxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hxx", ".-inl.h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hpp", ".hxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hh", ".hpp"); + } + m_modules.insert(std::make_pair(ss.str(), level)); + }; + bool isMod = true; + bool isLevel = false; + std::stringstream ss; + int level = -1; + for (; *modules; ++modules) { + switch (*modules) { + case '=': + isLevel = true; + isMod = false; + break; + case ',': + isLevel = false; + isMod = true; + if (!ss.str().empty() && level != -1) { + insert(ss, static_cast<base::type::VerboseLevel>(level)); + ss.str(std::string("")); + level = -1; + } + break; + default: + if (isMod) { + ss << *modules; + } else if (isLevel) { + if (isdigit(*modules)) { + level = static_cast<base::type::VerboseLevel>(*modules) - 48; + } + } + break; + } + } + if (!ss.str().empty() && level != -1) { + insert(ss, static_cast<base::type::VerboseLevel>(level)); + } +} + +void VRegistry::setCategories(const char* categories, bool clear) { + base::threading::ScopedLock scopedLock(lock()); + auto insert = [&](std::stringstream& ss, Level level) { + m_categories.push_back(std::make_pair(ss.str(), level)); + }; + + if (clear) + m_categories.clear(); + if (!categories) + return; + + bool isCat = true; + bool isLevel = false; + std::stringstream ss; + Level level = Level::Unknown; + for (; *categories; ++categories) { + switch (*categories) { + case ':': + isLevel = true; + isCat = false; + break; + case ',': + isLevel = false; + isCat = true; + if (!ss.str().empty() && level != Level::Unknown) { + insert(ss, level); + ss.str(std::string("")); + level = Level::Unknown; + } + break; + default: + if (isCat) { + ss << *categories; + } else if (isLevel) { + level = LevelHelper::convertFromStringPrefix(categories); + if (level != Level::Unknown) + categories += strlen(LevelHelper::convertToString(level)) - 1; + } + break; + } + } + if (!ss.str().empty() && level != Level::Unknown) { + insert(ss, level); + } +} + +// Log levels are sorted in a weird way... +static int priority(Level level) { + if (level == Level::Fatal) return 0; + if (level == Level::Error) return 1; + if (level == Level::Warning) return 2; + if (level == Level::Info) return 3; + if (level == Level::Debug) return 4; + if (level == Level::Verbose) return 5; + if (level == Level::Trace) return 6; + return 7; +} + +bool VRegistry::allowed(Level level, const char* category) { + base::threading::ScopedLock scopedLock(lock()); + if (m_categories.empty() || category == nullptr) { + return false; + } else { + std::deque<std::pair<std::string, Level>>::const_reverse_iterator it = m_categories.rbegin(); + for (; it != m_categories.rend(); ++it) { + if (base::utils::Str::wildCardMatch(category, it->first.c_str())) { + return priority(level) <= priority(it->second); + } + } + return false; + } +} + +bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) { + base::threading::ScopedLock scopedLock(lock()); + if (m_modules.empty() || file == nullptr) { + return vlevel <= m_level; + } else { + std::map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin(); + for (; it != m_modules.end(); ++it) { + if (base::utils::Str::wildCardMatch(file, it->first.c_str())) { + return vlevel <= it->second; + } + } + if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { + return true; + } + return false; + } +} + +void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) { + if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") || + commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) { + setLevel(base::consts::kMaxVerboseLevel); + } else if (commandLineArgs->hasParamWithValue("--v")) { + setLevel(static_cast<base::type::VerboseLevel>(atoi(commandLineArgs->getParamValue("--v")))); + } else if (commandLineArgs->hasParamWithValue("--V")) { + setLevel(static_cast<base::type::VerboseLevel>(atoi(commandLineArgs->getParamValue("--V")))); + } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-vmodule")); + } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-VMODULE")); + } +} + +#if !defined(ELPP_DEFAULT_LOGGING_FLAGS) +# define ELPP_DEFAULT_LOGGING_FLAGS 0x0 +#endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS) +// Storage +#if ELPP_ASYNC_LOGGING +Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : +#else +Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : +#endif // ELPP_ASYNC_LOGGING + m_registeredHitCounters(new base::RegisteredHitCounters()), + m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), + m_flags(ELPP_DEFAULT_LOGGING_FLAGS), + m_vRegistry(new base::VRegistry(0, &m_flags)), +#if ELPP_ASYNC_LOGGING + m_asyncLogQueue(new base::AsyncLogQueue()), + m_asyncDispatchWorker(asyncDispatchWorker), +#endif // ELPP_ASYNC_LOGGING + m_preRollOutCallback(base::defaultPreRollOutCallback) { + // Register default logger + m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); + // We register default logger anyway (worse case it's not going to register) just in case + m_registeredLoggers->get("default"); + // Register performance logger and reconfigure format + Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); + m_registeredLoggers->get("performance"); + performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); + performanceLogger->reconfigure(); +#if defined(ELPP_SYSLOG) + // Register syslog logger and reconfigure format + Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); + sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); + sysLogLogger->reconfigure(); +#endif // defined(ELPP_SYSLOG) + addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); + addFlag(LoggingFlag::CreateLoggerAutomatically); +#if ELPP_ASYNC_LOGGING + installLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback")); +#else + installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback")); +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + installPerformanceTrackingCallback<base::DefaultPerformanceTrackingCallback> + (std::string("DefaultPerformanceTrackingCallback")); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); +#if ELPP_ASYNC_LOGGING + m_asyncDispatchWorker->start(); +#endif // ELPP_ASYNC_LOGGING +} + +Storage::~Storage(void) { + ELPP_INTERNAL_INFO(4, "Destroying storage"); +#if ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); + uninstallLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback")); + installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback")); + ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); + base::utils::safeDelete(m_asyncDispatchWorker); + ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); + base::utils::safeDelete(m_asyncLogQueue); +#endif // ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); + base::utils::safeDelete(m_registeredHitCounters); + ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); + base::utils::safeDelete(m_registeredLoggers); + ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); + base::utils::safeDelete(m_vRegistry); +} + +bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(lock()); + return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), + formatSpecifier) != m_customFormatSpecifiers.end(); +} + +void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { + return; + } + base::threading::ScopedLock scopedLock(lock()); + m_customFormatSpecifiers.push_back(customFormatSpecifier); +} + +bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(lock()); + std::vector<CustomFormatSpecifier>::iterator it = std::find(m_customFormatSpecifiers.begin(), + m_customFormatSpecifiers.end(), formatSpecifier); + if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { + m_customFormatSpecifiers.erase(it); + return true; + } + return false; +} + +void Storage::setApplicationArguments(int argc, char** argv) { + m_commandLineArgs.setArgs(argc, argv); + m_vRegistry->setFromArgs(commandLineArgs()); + // default log file +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) { + Configurations c; + c.setGlobally(ConfigurationType::Filename, + std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam))); + registeredLoggers()->setDefaultConfigurations(c); + for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin(); + it != registeredLoggers()->end(); ++it) { + it->second->configure(c); + } + } +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) { + int userInput = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); + if (ELPP_DEFAULT_LOGGING_FLAGS == 0x0) { + m_flags = userInput; + } else { + base::utils::addFlag<base::type::EnumType>(userInput, &m_flags); + } + } +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +} + +// DefaultLogDispatchCallback + +void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { + m_data = data; + dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), + m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog)); +} + +void DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) { + if (m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog) { + if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { + base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream( + m_data->logMessage()->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) + || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) { + m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " + << m_data->logMessage()->logger()->id() << "]", false); + } + } + if (m_data->dispatchAction() != base::DispatchAction::FileOnlyLog) { + if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + } + } +#if defined(ELPP_SYSLOG) + else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (m_data->logMessage()->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (m_data->logMessage()->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (m_data->logMessage()->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (m_data->logMessage()->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (m_data->logMessage()->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +#endif // defined(ELPP_SYSLOG) +} + +#if ELPP_ASYNC_LOGGING + +// AsyncLogDispatchCallback + +void AsyncLogDispatchCallback::handle(const LogDispatchData* data) { + base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), + data->dispatchAction() == base::DispatchAction::NormalLog || data->dispatchAction() == base::DispatchAction::FileOnlyLog); + if ((data->dispatchAction() == base::DispatchAction::NormalLog || data->dispatchAction() == base::DispatchAction::FileOnlyLog) + && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + // Save resources and only queue if we want to write to file otherwise just ignore handler + if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) { + ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine)); + } +} + +// AsyncDispatchWorker +AsyncDispatchWorker::AsyncDispatchWorker() { + setContinueRunning(false); +} + +AsyncDispatchWorker::~AsyncDispatchWorker() { + setContinueRunning(false); + ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); + clean(); + ELPP_INTERNAL_INFO(6, "Log queue cleaned"); +} + +bool AsyncDispatchWorker::clean(void) { + std::mutex m; + std::unique_lock<std::mutex> lk(m); + cv.wait(lk, [] { return !ELPP->asyncLogQueue()->empty(); }); + emptyQueue(); + lk.unlock(); + cv.notify_one(); + return ELPP->asyncLogQueue()->empty(); +} + +void AsyncDispatchWorker::emptyQueue(void) { + while (!ELPP->asyncLogQueue()->empty()) { + AsyncLogItem data = ELPP->asyncLogQueue()->next(); + handle(&data); + base::threading::msleep(100); + } +} + +void AsyncDispatchWorker::start(void) { + base::threading::msleep(5000); // 5s (why?) + setContinueRunning(true); + std::thread t1(&AsyncDispatchWorker::run, this); + t1.join(); +} + +void AsyncDispatchWorker::handle(AsyncLogItem* logItem) { + LogDispatchData* data = logItem->data(); + LogMessage* logMessage = logItem->logMessage(); + Logger* logger = logMessage->logger(); + base::TypedConfigurations* conf = logger->typedConfigurations(); + base::type::string_t logLine = logItem->logLine(); + if (data->dispatchAction() == base::DispatchAction::NormalLog || data->dispatchAction() == base::DispatchAction::FileOnlyLog) { + if (conf->toFile(logMessage->level())) { + base::type::fstream_t* fs = conf->fileStream(logMessage->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << conf->filename(logMessage->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) { + logger->flush(logMessage->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false); + } + } + } +# if defined(ELPP_SYSLOG) + else if (data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (logMessage->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (logMessage->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (logMessage->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (logMessage->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (logMessage->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +# endif // defined(ELPP_SYSLOG) +} + +void AsyncDispatchWorker::run(void) { + while (continueRunning()) { + emptyQueue(); + base::threading::msleep(10); // 10ms + } +} +#endif // ELPP_ASYNC_LOGGING + +// DefaultLogBuilder + +base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool appendNewLine) const { + base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations(); + const base::LogFormat* logFormat = &tc->logFormat(logMessage->level()); + base::type::string_t logLine = logFormat->format(); + char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; + const char* bufLim = buff + sizeof(buff); + if (logFormat->hasFlag(base::FormatFlags::AppName)) { + // App name + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, + logMessage->logger()->parentApplicationName()); + } + if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { + // Thread ID + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, + ELPP->getThreadName(base::threading::getCurrentThreadId())); + } + if (logFormat->hasFlag(base::FormatFlags::DateTime)) { + // DateTime + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier, + base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), + &tc->subsecondPrecision(logMessage->level()))); + } + if (logFormat->hasFlag(base::FormatFlags::Function)) { + // Function + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func()); + } + if (logFormat->hasFlag(base::FormatFlags::File)) { + // File + base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff, ELPP->vRegistry()->getFilenameCommonPrefix()); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::FileBase)) { + // FileBase + base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildBaseFilename(logMessage->file(), buff); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Line)) { + // Line + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Location)) { + // Location + char* buf = base::utils::Str::clearBuff(buff, + base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff, ELPP->vRegistry()->getFilenameCommonPrefix()); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + buf = base::utils::Str::addToBuff(":", buf, bufLim); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, + false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); + } + if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { + // Verbose level + char* buf = base::utils::Str::clearBuff(buff, 1); + buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { + // Log message + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); + } +#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin(); + it != ELPP->customFormatSpecifiers()->end(); ++it) { + std::string fs(it->formatSpecifier()); + base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); + base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage)); + } +#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + if (appendNewLine) logLine += ELPP_LITERAL("\n"); + return logLine; +} + +// LogDispatcher + +void LogDispatcher::dispatch(void) { + if (m_proceed && m_dispatchAction == base::DispatchAction::None) { + m_proceed = false; + } + if (!m_proceed) { + return; + } + base::threading::ScopedLock scopedLock(ELPP->lock()); + base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations; + if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { + tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback()); + } + LogDispatchCallback* callback = nullptr; + LogDispatchData data; + for (const std::pair<std::string, base::type::LogDispatchCallbackPtr>& h + : ELPP->m_logDispatchCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + data.setLogMessage(&m_logMessage); + data.setDispatchAction(m_dispatchAction); + callback->handle(&data); + } + } +} + +// MessageBuilder + +void MessageBuilder::initialize(Logger* logger) { + m_logger = logger; + m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? + ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); +} + +MessageBuilder& MessageBuilder::operator<<(const wchar_t* msg) { + if (msg == nullptr) { + m_logger->stream() << base::consts::kNullPointer; + return *this; + } +# if defined(ELPP_UNICODE) + m_logger->stream() << msg; +# else + char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg); + m_logger->stream() << buff_; + free(buff_); +# endif + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; +} + +// Writer + +Writer& Writer::construct(Logger* logger, bool needLock) { + m_logger = logger; + initializeLogger(logger->id(), false, needLock); + m_messageBuilder.initialize(m_logger); + return *this; +} + +Writer& Writer::construct(int count, const char* loggerIds, ...) { + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + va_list loggersList; + va_start(loggersList, loggerIds); + const char* id = loggerIds; + for (int i = 0; i < count; ++i) { + m_loggerIds.push_back(std::string(id)); + id = va_arg(loggersList, const char*); + } + va_end(loggersList); + initializeLogger(m_loggerIds.at(0)); + } else { + initializeLogger(std::string(loggerIds)); + } + m_messageBuilder.initialize(m_logger); + return *this; +} + +void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) { + if (lookup) { + m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); + } + if (m_logger == nullptr) { + ELPP->acquireLock(); + if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { + // Somehow default logger has been unregistered. Not good! Register again + ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + } + ELPP->releaseLock(); // Need to unlock it for next writer + Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Logger [" << loggerId << "] is not registered yet!"; + m_proceed = false; + } else { + if (needLock) { + m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because + // m_proceed can be changed by lines below + } + if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { + m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : + ELPP->vRegistry()->allowed(m_level, loggerId.c_str()); + } else { + m_proceed = m_logger->enabled(m_level); + } + } +} + +void Writer::processDispatch() { +#if ELPP_LOGGING_ENABLED + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + bool firstDispatched = false; + base::type::string_t logMessage; + std::size_t i = 0; + do { + if (m_proceed) { + if (firstDispatched) { + m_logger->stream() << logMessage; + } else { + firstDispatched = true; + if (m_loggerIds.size() > 1) { + logMessage = m_logger->stream().str(); + } + } + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (i + 1 < m_loggerIds.size()) { + initializeLogger(m_loggerIds.at(i + 1)); + } + } while (++i < m_loggerIds.size()); + } else { + if (m_proceed) { + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + } +#else + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } +#endif // ELPP_LOGGING_ENABLED +} + +void Writer::triggerDispatch(void) { + if (m_proceed) { + base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel, + m_logger), m_dispatchAction).dispatch(); + } + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (m_proceed && m_level == Level::Fatal + && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { + base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; + std::stringstream reasonStream; + reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" + << " If you wish to disable 'abort on fatal log' please use " + << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; + base::utils::abort(1, reasonStream.str()); + } + m_proceed = false; +} + +// PErrorWriter + +PErrorWriter::~PErrorWriter(void) { + if (m_proceed) { +#if ELPP_COMPILER_MSVC + char buff[256]; + strerror_s(buff, 256, errno); + m_logger->stream() << ": " << buff << " [" << errno << "]"; +#else + m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]"; +#endif + } +} + +// PerformanceTracker + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +PerformanceTracker::PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit, + const std::string& loggerId, + bool scopedLog, Level level) : + m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog), + m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + // We store it locally so that if user happen to change configuration by the end of scope + // or before calling checkpoint, we still depend on state of configuraton at time of construction + el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false); + m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level); + if (m_enabled) { + base::utils::DateTime::gettimeofday(&m_startTime); + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED +} + +PerformanceTracker::~PerformanceTracker(void) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + if (m_scopedLog) { + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = getFormattedTimeTaken(); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); + data.init(this); + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(&data); + } + } + } + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) +} + +void PerformanceTracker::checkpoint(const std::string& id, const char* file, base::type::LineNumber line, + const char* func) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); + data.init(this); + data.m_checkpointId = id; + data.m_file = file; + data.m_line = line; + data.m_func = func; + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(&data); + } + } + base::utils::DateTime::gettimeofday(&m_lastCheckpointTime); + m_hasChecked = true; + m_lastCheckpointId = id; + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + ELPP_UNUSED(id); + ELPP_UNUSED(file); + ELPP_UNUSED(line); + ELPP_UNUSED(func); +} + +const base::type::string_t PerformanceTracker::getFormattedTimeTaken(struct timeval startTime) const { + if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { + base::type::stringstream_t ss; + ss << base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast<base::type::EnumType> + (m_timestampUnit)].unit; + return ss.str(); + } + return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit), m_timestampUnit); +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +// StackTrace + +StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, + const char* addr) { + m_index = index; + m_location = std::string(loc); + m_demangled = std::string(demang); + m_hex = std::string(hex); + m_addr = std::string(addr); +} + +std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) { + ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled + << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr; + return ss; +} + +std::ostream& operator<<(std::ostream& os, const StackTrace& st) { + std::vector<StackTrace::StackTraceEntry>::const_iterator it = st.m_stack.begin(); + while (it != st.m_stack.end()) { + os << " " << *it++ << "\n"; + } + return os; +} + +void StackTrace::generateNew(void) { +#if ELPP_STACKTRACE + m_stack.clear(); + void* stack[kMaxStack]; + unsigned int size = backtrace(stack, kMaxStack); + char** strings = backtrace_symbols(stack, size); + if (size > kStackStart) { // Skip StackTrace c'tor and generateNew + for (std::size_t i = kStackStart; i < size; ++i) { + char* mangName = nullptr; + char* hex = nullptr; + char* addr = nullptr; + for (char* c = strings[i]; *c; ++c) { + switch (*c) { + case '(': + mangName = c; + break; + case '+': + hex = c; + break; + case ')': + addr = c; + break; + default: + break; + } + } + // Perform demangling if parsed properly + if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) { + *mangName++ = '\0'; + *hex++ = '\0'; + *addr++ = '\0'; + int status = 0; + char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status); + // if demangling is successful, output the demangled function name + if (status == 0) { + // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) + StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr); + m_stack.push_back(entry); + } else { + // Not successful - we will use mangled name + StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr); + m_stack.push_back(entry); + } + free(demangName); + } else { + StackTraceEntry entry(i - 1, strings[i]); + m_stack.push_back(entry); + } + } + } + free(strings); +#else + ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); +#endif // ELPP_STACKTRACE +} + +// Static helper functions + +static std::string crashReason(int sig) { + std::stringstream ss; + bool foundReason = false; + for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { + if (base::consts::kCrashSignals[i].numb == sig) { + ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; + if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { + ss << std::endl << + " " << base::consts::kCrashSignals[i].brief << std::endl << + " " << base::consts::kCrashSignals[i].detail; + } + foundReason = true; + } + } + if (!foundReason) { + ss << "Application has crashed due to unknown signal [" << sig << "]"; + } + return ss.str(); +} +/// @brief Logs reason of crash from sig +static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + std::stringstream ss; + ss << "CRASH HANDLED; "; + ss << crashReason(sig); +#if ELPP_STACKTRACE + if (stackTraceIfAvailable) { + ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); + } +#else + ELPP_UNUSED(stackTraceIfAvailable); +#endif // ELPP_STACKTRACE + ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); +} + +static inline void crashAbort(int sig) { + base::utils::abort(sig, std::string()); +} + +/// @brief Default application crash handler +/// +/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program. +static inline void defaultCrashHandler(int sig) { + base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId); + base::debug::crashAbort(sig); +} + +// CrashHandler + +CrashHandler::CrashHandler(bool useDefault) { + if (useDefault) { + setHandler(defaultCrashHandler); + } +} + +void CrashHandler::setHandler(const Handler& cHandler) { + m_handler = cHandler; +#if defined(ELPP_HANDLE_SIGABRT) + int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] +#else + int i = 1; +#endif // defined(ELPP_HANDLE_SIGABRT) + for (; i < base::consts::kCrashSignalsCount; ++i) { + m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); + } +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base + +// el + +// Helpers + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +void Helpers::crashAbort(int sig, const char* sourceFile, unsigned int long line) { + std::stringstream ss; + ss << base::debug::crashReason(sig).c_str(); + ss << " - [Called el::Helpers::crashAbort(" << sig << ")]"; + if (sourceFile != nullptr && strlen(sourceFile) > 0) { + ss << " - Source: " << sourceFile; + if (line > 0) + ss << ":" << line; + else + ss << " (line number not specified)"; + } + base::utils::abort(sig, ss.str()); +} + +void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +// Loggers + +Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); +} + +void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) { + ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr); +} + +bool Loggers::unregisterLogger(const std::string& identity) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->remove(identity); +} + +bool Loggers::hasLogger(const std::string& identity) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->has(identity); +} + +Logger* Loggers::reconfigureLogger(Logger* logger, const Configurations& configurations) { + if (!logger) return nullptr; + logger->configure(configurations); + return logger; +} + +Logger* Loggers::reconfigureLogger(const std::string& identity, const Configurations& configurations) { + return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); +} + +Logger* Loggers::reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value) { + Logger* logger = Loggers::getLogger(identity); + if (logger == nullptr) { + return nullptr; + } + logger->configurations()->set(Level::Global, configurationType, value); + logger->reconfigure(); + return logger; +} + +void Loggers::reconfigureAllLoggers(const Configurations& configurations) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Loggers::reconfigureLogger(it->second, configurations); + } +} + +void Loggers::reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Logger* logger = it->second; + logger->configurations()->set(level, configurationType, value); + logger->reconfigure(); + } +} + +void Loggers::setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers) { + ELPP->registeredLoggers()->setDefaultConfigurations(configurations); + if (reconfigureExistingLoggers) { + Loggers::reconfigureAllLoggers(configurations); + } +} + +const Configurations* Loggers::defaultConfigurations(void) { + return ELPP->registeredLoggers()->defaultConfigurations(); +} + +const base::LogStreamsReferenceMap* Loggers::logStreamsReference(void) { + return ELPP->registeredLoggers()->logStreamsReference(); +} + +base::TypedConfigurations Loggers::defaultTypedConfigurations(void) { + return base::TypedConfigurations( + ELPP->registeredLoggers()->defaultConfigurations(), + ELPP->registeredLoggers()->logStreamsReference()); +} + +std::vector<std::string>* Loggers::populateAllLoggerIds(std::vector<std::string>* targetList) { + targetList->clear(); + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); + it != ELPP->registeredLoggers()->list().end(); ++it) { + targetList->push_back(it->first); + } + return targetList; +} + +void Loggers::configureFromGlobal(const char* globalConfigurationFilePath) { + std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in); + ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath + << "] for parsing."); + std::string line = std::string(); + std::stringstream ss; + Logger* logger = nullptr; + auto configure = [&](void) { + ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str() + << "\n--------------"); + Configurations c; + c.parseFromText(ss.str()); + logger->configure(c); + }; + while (gcfStream.good()) { + std::getline(gcfStream, line); + ELPP_INTERNAL_INFO(1, "Parsing line: " << line); + base::utils::Str::trim(line); + if (Configurations::Parser::isComment(line)) continue; + Configurations::Parser::ignoreComments(&line); + base::utils::Str::trim(line); + if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) { + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + ss.str(std::string("")); + line = line.substr(2); + base::utils::Str::trim(line); + if (line.size() > 1) { + ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'"); + logger = getLogger(line); + } + } else { + ss << line << "\n"; + } + } + if (!ss.str().empty() && logger != nullptr) { + configure(); + } +} + +bool Loggers::configureFromArg(const char* argKey) { +#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + ELPP_UNUSED(argKey); +#else + if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) { + return false; + } + configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey)); +#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + return true; +} + +void Loggers::flushAll(void) { + ELPP->registeredLoggers()->flushAll(); +} + +void Loggers::setVerboseLevel(base::type::VerboseLevel level) { + ELPP->vRegistry()->setLevel(level); +} + +base::type::VerboseLevel Loggers::verboseLevel(void) { + return ELPP->vRegistry()->level(); +} + +void Loggers::setVModules(const char* modules) { + if (ELPP->vRegistry()->vModulesEnabled()) { + ELPP->vRegistry()->setModules(modules); + } +} + +void Loggers::clearVModules(void) { + ELPP->vRegistry()->clearModules(); +} + +void Loggers::setCategories(const char* categories, bool clear) { + ELPP->vRegistry()->setCategories(categories, clear); +} + +void Loggers::clearCategories(void) { + ELPP->vRegistry()->clearCategories(); +} + +void Loggers::setFilenameCommonPrefix(const std::string &prefix) { + ELPP->vRegistry()->setFilenameCommonPrefix(prefix); +} + +const std::string &Loggers::getFilenameCommonPrefix() { + return ELPP->vRegistry()->getFilenameCommonPrefix(); +} + +// VersionInfo + +const std::string VersionInfo::version(void) { + return std::string("9.94.1"); +} +/// @brief Release date of current version +const std::string VersionInfo::releaseDate(void) { + return std::string("25-02-2017 0813hrs"); +} + +} // namespace el diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h index d28152667..195df7e5c 100644 --- a/external/easylogging++/easylogging++.h +++ b/external/easylogging++/easylogging++.h @@ -1,108 +1,108 @@ // // Bismillah ar-Rahmaan ar-Raheem // -// Easylogging++ v9.84 +// Easylogging++ v9.94.1 // Single-header only, cross-platform logging library for C++ applications // -// Copyright (c) 2016 muflihun.com +// Copyright (c) 2017 muflihun.com // // This library is released under the MIT Licence. -// http://easylogging.muflihun.com/licence.php +// http://labs.muflihun.com/easyloggingpp/licence.php // -// easylogging@muflihun.com -// -// https://github.com/easylogging/easyloggingpp -// http://easylogging.muflihun.com +// https://github.com/muflihun/easyloggingpp +// https://muflihun.github.io/easyloggingpp // http://muflihun.com // #ifndef EASYLOGGINGPP_H #define EASYLOGGINGPP_H +#include "ea_config.h" // Compilers and C++0x/C++11 Evaluation +#if __cplusplus >= 201103L +# define ELPP_CXX11 1 +#endif // __cplusplus >= 201103L #if (defined(__GNUC__)) -# define ELPP_COMPILER_GCC 1 +# define ELPP_COMPILER_GCC 1 #else -# define ELPP_COMPILER_GCC 0 +# define ELPP_COMPILER_GCC 0 #endif #if ELPP_COMPILER_GCC # define ELPP_GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) -# if defined(__GXX_EXPERIMENTAL_CXX0X__) -# define ELPP_CXX0X 1 -# elif(ELPP_GCC_VERSION >= 40801) -# define ELPP_CXX11 1 -# endif +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ELPP_CXX0X 1 +# endif #endif // Visual C++ #if defined(_MSC_VER) -# define ELPP_COMPILER_MSVC 1 +# define ELPP_COMPILER_MSVC 1 #else -# define ELPP_COMPILER_MSVC 0 +# define ELPP_COMPILER_MSVC 0 #endif #define ELPP_CRT_DBG_WARNINGS ELPP_COMPILER_MSVC #if ELPP_COMPILER_MSVC -# if (_MSC_VER == 1600) -# define ELPP_CXX0X 1 -# elif(_MSC_VER >= 1700) -# define ELPP_CXX11 1 -# endif +# if (_MSC_VER == 1600) +# define ELPP_CXX0X 1 +# elif(_MSC_VER >= 1700) +# define ELPP_CXX11 1 +# endif #endif // Clang++ #if (defined(__clang__) && (__clang__ == 1)) -# define ELPP_COMPILER_CLANG 1 +# define ELPP_COMPILER_CLANG 1 #else -# define ELPP_COMPILER_CLANG 0 +# define ELPP_COMPILER_CLANG 0 #endif #if ELPP_COMPILER_CLANG -# define ELPP_CLANG_VERSION (__clang_major__ * 10000 \ -+ __clang_minor__ * 100 \ -+ __clang_patchlevel__) -# if (ELPP_CLANG_VERSION >= 30300) -# define ELPP_CXX11 1 -# endif // (ELPP_CLANG_VERSION >= 30300) +# if __has_include(<thread>) +# include <cstddef> // Make __GLIBCXX__ defined when using libstdc++ +# if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# define ELPP_CLANG_SUPPORTS_THREAD +# endif // !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# endif // __has_include(<thread>) #endif #if (defined(__MINGW32__) || defined(__MINGW64__)) -# define ELPP_MINGW 1 +# define ELPP_MINGW 1 #else -# define ELPP_MINGW 0 +# define ELPP_MINGW 0 #endif #if (defined(__CYGWIN__) && (__CYGWIN__ == 1)) -# define ELPP_CYGWIN 1 +# define ELPP_CYGWIN 1 #else -# define ELPP_CYGWIN 0 +# define ELPP_CYGWIN 0 #endif #if (defined(__INTEL_COMPILER)) -# define ELPP_COMPILER_INTEL 1 +# define ELPP_COMPILER_INTEL 1 #else -# define ELPP_COMPILER_INTEL 0 +# define ELPP_COMPILER_INTEL 0 #endif // Operating System Evaluation // Windows #if (defined(_WIN32) || defined(_WIN64)) -# define ELPP_OS_WINDOWS 1 +# define ELPP_OS_WINDOWS 1 #else -# define ELPP_OS_WINDOWS 0 +# define ELPP_OS_WINDOWS 0 #endif // Linux #if (defined(__linux) || defined(__linux__)) -# define ELPP_OS_LINUX 1 +# define ELPP_OS_LINUX 1 #else -# define ELPP_OS_LINUX 0 +# define ELPP_OS_LINUX 0 #endif #if (defined(__APPLE__)) -# define ELPP_OS_MAC 1 +# define ELPP_OS_MAC 1 #else -# define ELPP_OS_MAC 0 +# define ELPP_OS_MAC 0 #endif #if (defined(__FreeBSD__)) -# define ELPP_OS_FREEBSD 1 +# define ELPP_OS_FREEBSD 1 #else -# define ELPP_OS_FREEBSD 0 +# define ELPP_OS_FREEBSD 0 #endif #if (defined(__sun)) -# define ELPP_OS_SOLARIS 1 +# define ELPP_OS_SOLARIS 1 #else -# define ELPP_OS_SOLARIS 0 +# define ELPP_OS_SOLARIS 0 #endif #if (defined(__DragonFly__)) # define ELPP_OS_DRAGONFLY 1 @@ -111,114 +111,118 @@ #endif // Unix #if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS || ELPP_OS_DRAGONFLY) && (!ELPP_OS_WINDOWS)) -# define ELPP_OS_UNIX 1 +# define ELPP_OS_UNIX 1 #else -# define ELPP_OS_UNIX 0 +# define ELPP_OS_UNIX 0 #endif #if (defined(__ANDROID__)) -# define ELPP_OS_ANDROID 1 +# define ELPP_OS_ANDROID 1 #else -# define ELPP_OS_ANDROID 0 +# define ELPP_OS_ANDROID 0 #endif // Evaluating Cygwin as *nix OS #if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN -# undef ELPP_OS_UNIX -# undef ELPP_OS_LINUX -# define ELPP_OS_UNIX 1 -# define ELPP_OS_LINUX 1 +# undef ELPP_OS_UNIX +# undef ELPP_OS_LINUX +# define ELPP_OS_UNIX 1 +# define ELPP_OS_LINUX 1 #endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN #if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO) -# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout +# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout #endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) #if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR) -# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr +# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr #endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) #if !defined(ELPP_INTERNAL_DEBUGGING_ENDL) -# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl +# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl #endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) #if !defined(ELPP_INTERNAL_DEBUGGING_MSG) -# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg +# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg #endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) // Internal Assertions and errors #if !defined(ELPP_DISABLE_ASSERT) -# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) -# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ std::stringstream internalInfoStream; internalInfoStream << msg; \ ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ << "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \ "ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); } -# else -# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +# else +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ std::stringstream internalInfoStream; internalInfoStream << msg; \ ELPP_INTERNAL_DEBUGGING_OUT_ERROR\ << "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \ << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \ << ELPP_INTERNAL_DEBUGGING_ENDL; } -# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) #else -# define ELPP_ASSERT(x, y) +# define ELPP_ASSERT(x, y) #endif //(!defined(ELPP_DISABLE_ASSERT) #if ELPP_COMPILER_MSVC -# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ { char buff[256]; strerror_s(buff, 256, errno); \ ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0 #else -# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0 #endif // ELPP_COMPILER_MSVC #if defined(ELPP_DEBUG_ERRORS) -# if !defined(ELPP_INTERNAL_ERROR) -# define ELPP_INTERNAL_ERROR(msg, pe) { \ +# if !defined(ELPP_INTERNAL_ERROR) +# define ELPP_INTERNAL_ERROR(msg, pe) { \ std::stringstream internalInfoStream; internalInfoStream << "<ERROR> " << msg; \ ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ << "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \ << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \ if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0 -# endif +# endif #else -# undef ELPP_INTERNAL_INFO -# define ELPP_INTERNAL_ERROR(msg, pe) +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_ERROR(msg, pe) #endif // defined(ELPP_DEBUG_ERRORS) #if (defined(ELPP_DEBUG_INFO)) -# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) -# define ELPP_INTERNAL_INFO_LEVEL 9 -# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) -# if !defined(ELPP_INTERNAL_INFO) -# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ +# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# define ELPP_INTERNAL_INFO_LEVEL 9 +# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# if !defined(ELPP_INTERNAL_INFO) +# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ std::stringstream internalInfoStream; internalInfoStream << "<INFO> " << msg; \ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ << ELPP_INTERNAL_DEBUGGING_ENDL; }} -# endif +# endif #else -# undef ELPP_INTERNAL_INFO -# define ELPP_INTERNAL_INFO(lvl, msg) +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_INFO(lvl, msg) #endif // (defined(ELPP_DEBUG_INFO)) -#if defined(ELPP_STACKTRACE_ON_CRASH) -# if (ELPP_COMPILER_GCC && !ELPP_MINGW) -# define ELPP_STACKTRACE 1 -# else +#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW) +# define ELPP_STACKTRACE 1 +# else +# define ELPP_STACKTRACE 0 # if ELPP_COMPILER_MSVC # pragma message("Stack trace not available for this compiler") # else # warning "Stack trace not available for this compiler"; # endif // ELPP_COMPILER_MSVC -# endif // ELPP_COMPILER_GCC -#endif // (defined(ELPP_STACKTRACE_ON_CRASH)) +# define ELPP_STACKTRACE 0 +# endif // ELPP_COMPILER_GCC +#else +# define ELPP_STACKTRACE 0 +#endif // (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) // Miscellaneous macros #define ELPP_UNUSED(x) (void)x #if ELPP_OS_UNIX // Log file permissions for unix-based systems -# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH +# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH #endif // ELPP_OS_UNIX #if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC -# if defined(ELPP_EXPORT_SYMBOLS) -# define ELPP_EXPORT __declspec(dllexport) -# else -# define ELPP_EXPORT __declspec(dllimport) -# endif // defined(ELPP_EXPORT_SYMBOLS) +# if defined(ELPP_EXPORT_SYMBOLS) +# define ELPP_EXPORT __declspec(dllexport) +# else +# define ELPP_EXPORT __declspec(dllimport) +# endif // defined(ELPP_EXPORT_SYMBOLS) #else -# define ELPP_EXPORT +# define ELPP_EXPORT #endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC // Some special functions that are VC++ specific #undef STRTOK @@ -226,54 +230,60 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #undef STRCAT #undef STRCPY #if ELPP_CRT_DBG_WARNINGS -# define STRTOK(a, b, c) strtok_s(a, b, c) -# define STRERROR(a, b, c) strerror_s(a, b, c) -# define STRCAT(a, b, len) strcat_s(a, len, b) -# define STRCPY(a, b, len) strcpy_s(a, len, b) +# define STRTOK(a, b, c) strtok_s(a, b, c) +# define STRERROR(a, b, c) strerror_s(a, b, c) +# define STRCAT(a, b, len) strcat_s(a, len, b) +# define STRCPY(a, b, len) strcpy_s(a, len, b) #else -# define STRTOK(a, b, c) strtok(a, b) -# define STRERROR(a, b, c) strerror(c) -# define STRCAT(a, b, len) strcat(a, b) -# define STRCPY(a, b, len) strcpy(a, b) +# define STRTOK(a, b, c) strtok(a, b) +# define STRERROR(a, b, c) strerror(c) +# define STRCAT(a, b, len) strcat(a, b) +# define STRCPY(a, b, len) strcpy(a, b) #endif // Compiler specific support evaluations -#if ((!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD)) -# define ELPP_USE_STD_THREADING 1 +#if (ELPP_MINGW && !defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 0 #else -# define ELPP_USE_STD_THREADING 0 +# if ((ELPP_COMPILER_CLANG && defined(ELPP_CLANG_SUPPORTS_THREAD)) || \ + (!ELPP_COMPILER_CLANG && defined(ELPP_CXX11)) || \ + defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 1 +# else +# define ELPP_USE_STD_THREADING 0 +# endif #endif #undef ELPP_FINAL #if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) -# define ELPP_FINAL +# define ELPP_FINAL #else -# define ELPP_FINAL final +# define ELPP_FINAL final #endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) #if defined(ELPP_EXPERIMENTAL_ASYNC) -# define ELPP_ASYNC_LOGGING 1 +# define ELPP_ASYNC_LOGGING 1 #else -# define ELPP_ASYNC_LOGGING 0 +# define ELPP_ASYNC_LOGGING 0 #endif // defined(ELPP_EXPERIMENTAL_ASYNC) #if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING -# define ELPP_THREADING_ENABLED 1 +# define ELPP_THREADING_ENABLED 1 #else -# define ELPP_THREADING_ENABLED 0 +# define ELPP_THREADING_ENABLED 0 #endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING // Function macro ELPP_FUNC #undef ELPP_FUNC #if ELPP_COMPILER_MSVC // Visual C++ -# define ELPP_FUNC __FUNCSIG__ +# define ELPP_FUNC __FUNCSIG__ #elif ELPP_COMPILER_GCC // GCC -# define ELPP_FUNC __PRETTY_FUNCTION__ +# define ELPP_FUNC __PRETTY_FUNCTION__ #elif ELPP_COMPILER_INTEL // Intel C++ -# define ELPP_FUNC __PRETTY_FUNCTION__ +# define ELPP_FUNC __PRETTY_FUNCTION__ #elif ELPP_COMPILER_CLANG // Clang++ -# define ELPP_FUNC __PRETTY_FUNCTION__ +# define ELPP_FUNC __PRETTY_FUNCTION__ #else -# if defined(__func__) -# define ELPP_FUNC __func__ -# else -# define ELPP_FUNC "" -# endif // defined(__func__) +# if defined(__func__) +# define ELPP_FUNC __func__ +# else +# define ELPP_FUNC "" +# endif // defined(__func__) #endif // defined(_MSC_VER) #undef ELPP_VARIADIC_TEMPLATES_SUPPORTED // Keep following line commented until features are fixed @@ -281,43 +291,43 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre (ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)) // Logging Enable/Disable macros #define ELPP_LOGGING_ENABLED (!defined(ELPP_DISABLE_LOGS)) -#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG)))) -# define ELPP_DEBUG_LOG 1 +#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_DEBUG_LOG 1 #else -# define ELPP_DEBUG_LOG 0 -#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG)))) +# define ELPP_DEBUG_LOG 0 +#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_INFO_LOG 1 +# define ELPP_INFO_LOG 1 #else -# define ELPP_INFO_LOG 0 +# define ELPP_INFO_LOG 0 #endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_WARNING_LOG 1 +# define ELPP_WARNING_LOG 1 #else -# define ELPP_WARNING_LOG 0 +# define ELPP_WARNING_LOG 0 #endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_ERROR_LOG 1 +# define ELPP_ERROR_LOG 1 #else -# define ELPP_ERROR_LOG 0 +# define ELPP_ERROR_LOG 0 #endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_FATAL_LOG 1 +# define ELPP_FATAL_LOG 1 #else -# define ELPP_FATAL_LOG 0 +# define ELPP_FATAL_LOG 0 #endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_TRACE_LOG 1 +# define ELPP_TRACE_LOG 1 #else -# define ELPP_TRACE_LOG 0 +# define ELPP_TRACE_LOG 0 #endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) -# define ELPP_VERBOSE_LOG 1 +# define ELPP_VERBOSE_LOG 1 #else -# define ELPP_VERBOSE_LOG 0 +# define ELPP_VERBOSE_LOG 0 #endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) #if (!(ELPP_CXX0X || ELPP_CXX11)) -# error "Easylogging++ 9.0+ is only compatible with C++0x (or higher) compliant compiler" +# error "C++0x (or higher) support not detected! (Is `-std=c++11' missing?)" #endif // (!(ELPP_CXX0X || ELPP_CXX11)) // Headers #if defined(ELPP_SYSLOG) @@ -333,9 +343,9 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #include <cstdarg> #if defined(ELPP_UNICODE) # include <locale> -# if ELPP_OS_WINDOWS +# if ELPP_OS_WINDOWS # include <codecvt> -# endif // ELPP_OS_WINDOWS +# endif // ELPP_OS_WINDOWS #endif // defined(ELPP_UNICODE) #if ELPP_STACKTRACE # include <cxxabi.h> @@ -350,13 +360,13 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #elif ELPP_OS_WINDOWS # include <direct.h> # include <windows.h> -# if defined(WIN32_LEAN_AND_MEAN) +# if defined(WIN32_LEAN_AND_MEAN) # if defined(ELPP_WINSOCK2) # include <winsock2.h> # else # include <winsock.h> # endif // defined(ELPP_WINSOCK2) -# endif // defined(WIN32_LEAN_AND_MEAN) +# endif // defined(WIN32_LEAN_AND_MEAN) #endif // ELPP_OS_UNIX #include <string> #include <vector> @@ -368,23 +378,22 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #include <fstream> #include <iostream> #include <sstream> -#include <iomanip> #include <memory> #include <type_traits> #if ELPP_THREADING_ENABLED -# if ELPP_USE_STD_THREADING +# if ELPP_USE_STD_THREADING # include <mutex> # include <thread> -# else +# else # if ELPP_OS_UNIX # include <pthread.h> # endif // ELPP_OS_UNIX -# endif // ELPP_USE_STD_THREADING +# endif // ELPP_USE_STD_THREADING #endif // ELPP_THREADING_ENABLED #if ELPP_ASYNC_LOGGING -# if defined(ELPP_NO_SLEEP_FOR) +# if defined(ELPP_NO_SLEEP_FOR) # include <unistd.h> -# endif // defined(ELPP_NO_SLEEP_FOR) +# endif // defined(ELPP_NO_SLEEP_FOR) # include <thread> # include <queue> # include <condition_variable> @@ -397,15 +406,15 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre # include <set> # include <bitset> # include <stack> -# if defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_STD_ARRAY) # include <array> -# endif // defined(ELPP_LOG_STD_ARRAY) -# if defined(ELPP_LOG_UNORDERED_MAP) +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) # include <unordered_map> -# endif // defined(ELPP_LOG_UNORDERED_MAP) -# if defined(ELPP_LOG_UNORDERED_SET) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) # include <unordered_set> -# endif // defined(ELPP_UNORDERED_SET) +# endif // defined(ELPP_UNORDERED_SET) #endif // defined(ELPP_STL_LOGGING) #if defined(ELPP_QT_LOGGING) // For logging Qt based classes & templates @@ -439,1565 +448,971 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre #endif // defined(ELPP_WXWIDGETS_LOGGING) // Forward declarations namespace el { - class Logger; - class LogMessage; - class PerformanceTrackingData; - class Loggers; - class Helpers; - template <typename T> class Callback; - class LogDispatchCallback; - class PerformanceTrackingCallback; - class LogDispatchData; - namespace base { - class Storage; - class RegisteredLoggers; - class PerformanceTracker; - class MessageBuilder; - class Writer; - class PErrorWriter; - class LogDispatcher; - class DefaultLogBuilder; - class DefaultLogDispatchCallback; +class Logger; +class LogMessage; +class PerformanceTrackingData; +class Loggers; +class Helpers; +template <typename T> class Callback; +class LogDispatchCallback; +class PerformanceTrackingCallback; +class LoggerRegistrationCallback; +class LogDispatchData; +namespace base { +class Storage; +class RegisteredLoggers; +class PerformanceTracker; +class MessageBuilder; +class Writer; +class PErrorWriter; +class LogDispatcher; +class DefaultLogBuilder; +class DefaultLogDispatchCallback; #if ELPP_ASYNC_LOGGING - class AsyncLogDispatchCallback; - class AsyncDispatchWorker; +class AsyncLogDispatchCallback; +class AsyncDispatchWorker; #endif // ELPP_ASYNC_LOGGING - class DefaultPerformanceTrackingCallback; - } // namespace base +class DefaultPerformanceTrackingCallback; +} // namespace base } // namespace el /// @brief Easylogging++ entry namespace namespace el { - /// @brief Namespace containing base/internal functionality used by Easylogging++ - namespace base { - /// @brief Data types used by Easylogging++ - namespace type { +/// @brief Namespace containing base/internal functionality used by Easylogging++ +namespace base { +/// @brief Data types used by Easylogging++ +namespace type { #undef ELPP_LITERAL #undef ELPP_STRLEN #undef ELPP_COUT #if defined(ELPP_UNICODE) -# define ELPP_LITERAL(txt) L##txt -# define ELPP_STRLEN wcslen -# if defined ELPP_CUSTOM_COUT -# define ELPP_COUT ELPP_CUSTOM_COUT -# else -# define ELPP_COUT std::wcout -# endif // defined ELPP_CUSTOM_COUT - typedef wchar_t char_t; - typedef std::wstring string_t; - typedef std::wstringstream stringstream_t; - typedef std::wfstream fstream_t; - typedef std::wostream ostream_t; +# define ELPP_LITERAL(txt) L##txt +# define ELPP_STRLEN wcslen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::wcout +# endif // defined ELPP_CUSTOM_COUT +typedef wchar_t char_t; +typedef std::wstring string_t; +typedef std::wstringstream stringstream_t; +typedef std::wfstream fstream_t; +typedef std::wostream ostream_t; #else -# define ELPP_LITERAL(txt) txt -# define ELPP_STRLEN strlen -# if defined ELPP_CUSTOM_COUT -# define ELPP_COUT ELPP_CUSTOM_COUT -# else -# define ELPP_COUT std::cout -# endif // defined ELPP_CUSTOM_COUT - typedef char char_t; - typedef std::string string_t; - typedef std::stringstream stringstream_t; - typedef std::fstream fstream_t; - typedef std::ostream ostream_t; +# define ELPP_LITERAL(txt) txt +# define ELPP_STRLEN strlen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::cout +# endif // defined ELPP_CUSTOM_COUT +typedef char char_t; +typedef std::string string_t; +typedef std::stringstream stringstream_t; +typedef std::fstream fstream_t; +typedef std::ostream ostream_t; #endif // defined(ELPP_UNICODE) #if defined(ELPP_CUSTOM_COUT_LINE) -# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) +# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) #else -# define ELPP_COUT_LINE(logLine) logLine << std::flush +# define ELPP_COUT_LINE(logLine) logLine << std::flush #endif // defined(ELPP_CUSTOM_COUT_LINE) - typedef unsigned short EnumType; - typedef std::shared_ptr<base::Storage> StoragePointer; - typedef int VerboseLevel; - typedef std::shared_ptr<LogDispatchCallback> LogDispatchCallbackPtr; - typedef std::shared_ptr<PerformanceTrackingCallback> PerformanceTrackingCallbackPtr; - } // namespace type - /// @brief Internal helper class that prevent copy constructor for class - /// - /// @detail When using this class simply inherit it privately - class NoCopy { - protected: - NoCopy(void) {} - private: - NoCopy(const NoCopy&); - NoCopy& operator=(const NoCopy&); - }; - /// @brief Internal helper class that makes all default constructors private. - /// - /// @detail This prevents initializing class making it static unless an explicit constructor is declared. - /// When using this class simply inherit it privately - class StaticClass { - private: - StaticClass(void); - StaticClass(const StaticClass&); - StaticClass& operator=(const StaticClass&); - }; - } // namespace base - /// @brief Represents enumeration for severity level used to determine level of logging - /// - /// @detail With Easylogging++, developers may disable or enable any level regardless of - /// what the severity is. Or they can choose to log using hierarchical logging flag - enum class Level : base::type::EnumType { - /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels - Global = 1, - /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. - Trace = 2, - /// @brief Informational events most useful for developers to debug application - Debug = 4, - /// @brief Severe error information that will presumably abort application - Fatal = 8, - /// @brief Information representing errors in application but application will keep running - Error = 16, - /// @brief Useful when application has potentially harmful situtaions - Warning = 32, - /// @brief Information that can be highly useful and vary with verbose logging level. - Verbose = 64, - /// @brief Mainly useful to represent current progress of application - Info = 128, - /// @brief Represents unknown level - Unknown = 1010 - }; - /// @brief Static class that contains helper functions for el::Level - class LevelHelper : base::StaticClass { - public: - /// @brief Represents minimum valid level. Useful when iterating through enum. - static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(Level::Trace); - /// @brief Represents maximum valid level. This is used internally and you should not need it. - static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(Level::Info); - /// @brief Casts level to int, useful for iterating through enum. - static base::type::EnumType castToInt(Level level) { - return static_cast<base::type::EnumType>(level); - } - /// @brief Casts int(ushort) to level, useful for iterating through enum. - static Level castFromInt(base::type::EnumType l) { - return static_cast<Level>(l); - } - /// @brief Converts level to associated const char* - /// @return Upper case string based level. - static const char* convertToString(Level level) { - // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. - if (level == Level::Global) return "GLOBAL"; - if (level == Level::Debug) return "DEBUG"; - if (level == Level::Info) return "INFO"; - if (level == Level::Warning) return "WARNING"; - if (level == Level::Error) return "ERROR"; - if (level == Level::Fatal) return "FATAL"; - if (level == Level::Verbose) return "VERBOSE"; - if (level == Level::Trace) return "TRACE"; - return "UNKNOWN"; - } - /// @brief Converts from levelStr to Level - /// @param levelStr Upper case string based level. - /// Lower case is also valid but providing upper case is recommended. - static Level convertFromString(const char* levelStr) { - if ((strcmp(levelStr, "GLOBAL") == 0) || (strcmp(levelStr, "global") == 0)) - return Level::Global; - if ((strcmp(levelStr, "DEBUG") == 0) || (strcmp(levelStr, "debug") == 0)) - return Level::Debug; - if ((strcmp(levelStr, "INFO") == 0) || (strcmp(levelStr, "info") == 0)) - return Level::Info; - if ((strcmp(levelStr, "WARNING") == 0) || (strcmp(levelStr, "warning") == 0)) - return Level::Warning; - if ((strcmp(levelStr, "ERROR") == 0) || (strcmp(levelStr, "error") == 0)) - return Level::Error; - if ((strcmp(levelStr, "FATAL") == 0) || (strcmp(levelStr, "fatal") == 0)) - return Level::Fatal; - if ((strcmp(levelStr, "VERBOSE") == 0) || (strcmp(levelStr, "verbose") == 0)) - return Level::Verbose; - if ((strcmp(levelStr, "TRACE") == 0) || (strcmp(levelStr, "trace") == 0)) - return Level::Trace; - return Level::Unknown; - } - /// @brief Converts from prefix of levelStr to Level - /// @param levelStr Upper case string based level. - /// Lower case is also valid but providing upper case is recommended. - static Level convertFromStringPrefix(const char* levelStr) { - if ((strncmp(levelStr, "GLOBAL", 6) == 0) || (strncmp(levelStr, "global", 6) == 0)) - return Level::Global; - if ((strncmp(levelStr, "DEBUG", 5) == 0) || (strncmp(levelStr, "debug", 5) == 0)) - return Level::Debug; - if ((strncmp(levelStr, "INFO", 4) == 0) || (strncmp(levelStr, "info", 4) == 0)) - return Level::Info; - if ((strncmp(levelStr, "WARNING", 7) == 0) || (strncmp(levelStr, "warning", 7) == 0)) - return Level::Warning; - if ((strncmp(levelStr, "ERROR", 5) == 0) || (strncmp(levelStr, "error", 5) == 0)) - return Level::Error; - if ((strncmp(levelStr, "FATAL", 5) == 0) || (strncmp(levelStr, "fatal", 5) == 0)) - return Level::Fatal; - if ((strncmp(levelStr, "VERBOSE", 7) == 0) || (strncmp(levelStr, "verbose", 7) == 0)) - return Level::Verbose; - if ((strncmp(levelStr, "TRACE", 5) == 0) || (strncmp(levelStr, "trace", 5) == 0)) - return Level::Trace; - return Level::Unknown; - } - /// @brief Applies specified function to each level starting from startIndex - /// @param startIndex initial value to start the iteration from. This is passed as pointer and - /// is left-shifted so this can be used inside function (fn) to represent current level. - /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. - static inline void forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) { - base::type::EnumType lIndexMax = LevelHelper::kMaxValid; - do { - if (fn()) { - break; - } - *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); - } while (*startIndex <= lIndexMax); - } - }; - /// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect - /// of logging - enum class ConfigurationType : base::type::EnumType { - /// @brief Determines whether or not corresponding level and logger of logging is enabled - /// You may disable all logs by using el::Level::Global - Enabled = 1, - /// @brief Whether or not to write corresponding log to log file - ToFile = 2, - /// @brief Whether or not to write corresponding level and logger log to standard output. - /// By standard output meaning termnal, command prompt etc - ToStandardOutput = 4, - /// @brief Determines format of logging corresponding level and logger. - Format = 8, - /// @brief Determines log file (full path) to write logs to for correponding level and logger - Filename = 16, - /// @brief Specifies milliseconds width. Width can be within range (1-6) - MillisecondsWidth = 32, - /// @brief Determines whether or not performance tracking is enabled. - /// - /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger - PerformanceTracking = 64, - /// @brief Specifies log file max size. - /// - /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will - /// be truncated and re-initiated. - MaxLogFileSize = 128, - /// @brief Specifies number of log entries to hold until we flush pending log data - LogFlushThreshold = 256, - /// @brief Represents unknown configuration - Unknown = 1010 - }; - /// @brief Static class that contains helper functions for el::ConfigurationType - class ConfigurationTypeHelper : base::StaticClass { - public: - /// @brief Represents minimum valid configuration type. Useful when iterating through enum. - static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(ConfigurationType::Enabled); - /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. - static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(ConfigurationType::MaxLogFileSize); - /// @brief Casts configuration type to int, useful for iterating through enum. - static base::type::EnumType castToInt(ConfigurationType configurationType) { - return static_cast<base::type::EnumType>(configurationType); - } - /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum. - static ConfigurationType castFromInt(base::type::EnumType c) { - return static_cast<ConfigurationType>(c); - } - /// @brief Converts configuration type to associated const char* - /// @returns Upper case string based configuration type. - static const char* convertToString(ConfigurationType configurationType) { - // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. - if (configurationType == ConfigurationType::Enabled) return "ENABLED"; - if (configurationType == ConfigurationType::Filename) return "FILENAME"; - if (configurationType == ConfigurationType::Format) return "FORMAT"; - if (configurationType == ConfigurationType::ToFile) return "TO_FILE"; - if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT"; - if (configurationType == ConfigurationType::MillisecondsWidth) return "MILLISECONDS_WIDTH"; - if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING"; - if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE"; - if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD"; - return "UNKNOWN"; - } - /// @brief Converts from configStr to ConfigurationType - /// @param configStr Upper case string based configuration type. - /// Lower case is also valid but providing upper case is recommended. - static ConfigurationType convertFromString(const char* configStr) { - if ((strcmp(configStr, "ENABLED") == 0) || (strcmp(configStr, "enabled") == 0)) - return ConfigurationType::Enabled; - if ((strcmp(configStr, "TO_FILE") == 0) || (strcmp(configStr, "to_file") == 0)) - return ConfigurationType::ToFile; - if ((strcmp(configStr, "TO_STANDARD_OUTPUT") == 0) || (strcmp(configStr, "to_standard_output") == 0)) - return ConfigurationType::ToStandardOutput; - if ((strcmp(configStr, "FORMAT") == 0) || (strcmp(configStr, "format") == 0)) - return ConfigurationType::Format; - if ((strcmp(configStr, "FILENAME") == 0) || (strcmp(configStr, "filename") == 0)) - return ConfigurationType::Filename; - if ((strcmp(configStr, "MILLISECONDS_WIDTH") == 0) || (strcmp(configStr, "milliseconds_width") == 0)) - return ConfigurationType::MillisecondsWidth; - if ((strcmp(configStr, "PERFORMANCE_TRACKING") == 0) || (strcmp(configStr, "performance_tracking") == 0)) - return ConfigurationType::PerformanceTracking; - if ((strcmp(configStr, "MAX_LOG_FILE_SIZE") == 0) || (strcmp(configStr, "max_log_file_size") == 0)) - return ConfigurationType::MaxLogFileSize; - if ((strcmp(configStr, "LOG_FLUSH_THRESHOLD") == 0) || (strcmp(configStr, "log_flush_threshold") == 0)) - return ConfigurationType::LogFlushThreshold; - return ConfigurationType::Unknown; - } - /// @brief Applies specified function to each configuration type starting from startIndex - /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted - /// so this can be used inside function (fn) to represent current configuration type. - /// @param fn function to apply with each configuration type. - /// This bool represent whether or not to stop iterating through configurations. - static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) { - base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; - do { - if (fn()) { - break; - } - *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); - } while (*startIndex <= cIndexMax); - } - }; - /// @brief Flags used while writing logs. This flags are set by user - enum class LoggingFlag : base::type::EnumType { - /// @brief Makes sure we have new line for each container log entry - NewLineForContainer = 1, - /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose - /// logging is allowed via that module. - AllowVerboseIfModuleNotSpecified = 2, - /// @brief When handling crashes by default, detailed crash reason will be logged as well - LogDetailedCrashReason = 4, - /// @brief Allows to disable application abortion when logged using FATAL level - DisableApplicationAbortOnFatalLog = 8, - /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default - ImmediateFlush = 16, - /// @brief Enables strict file rolling - StrictLogFileSizeCheck = 32, - /// @brief Make terminal output colorful for supported terminals - ColoredTerminalOutput = 64, - /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") - MultiLoggerSupport = 128, - /// @brief Disables comparing performance tracker's checkpoints - DisablePerformanceTrackingCheckpointComparison = 256, - /// @brief Disable VModules - DisableVModules = 512, - /// @brief Disable VModules extensions - DisableVModulesExtensions = 1024, - /// @brief Enables hierarchical logging - HierarchicalLogging = 2048, - /// @brief Creates logger automatically when not available - CreateLoggerAutomatically = 4096, - /// @brief Adds spaces b/w logs that separated by left-shift operator - AutoSpacing = 8192, - /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) - FixedTimeFormat = 16384 - }; - namespace base { - /// @brief Namespace containing constants used internally. - namespace consts { - // Level log values - These are values that are replaced in place of %level format specifier - static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO "); - static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); - static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARN "); - static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); - static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); - static const base::type::char_t* kVerboseLevelLogValue = ELPP_LITERAL("VER"); - static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); - static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); - static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); - static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); - static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); - static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); - static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); - static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); - // Format specifiers - These are used to define log format - static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); - static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); - static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); - static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); - static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); - static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); - static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); - static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); - static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); - static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); - static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); - static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); - static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); - static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); - static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); - static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; - // Date/time - static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; - static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August", - "September", "October", "November", "December" }; - static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; - static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; - static const int kYearBase = 1900; - static const char* kAm = "AM"; - static const char* kPm = "PM"; - // Miscellaneous constants - static const char* kDefaultLoggerId = "default"; - static const char* kPerformanceLoggerId = "performance"; +typedef unsigned int EnumType; +typedef unsigned short VerboseLevel; +typedef unsigned long int LineNumber; +typedef std::shared_ptr<base::Storage> StoragePointer; +typedef std::shared_ptr<LogDispatchCallback> LogDispatchCallbackPtr; +typedef std::shared_ptr<PerformanceTrackingCallback> PerformanceTrackingCallbackPtr; +typedef std::shared_ptr<LoggerRegistrationCallback> LoggerRegistrationCallbackPtr; +typedef std::unique_ptr<el::base::PerformanceTracker> PerformanceTrackerPtr; +} // namespace type +/// @brief Internal helper class that prevent copy constructor for class +/// +/// @detail When using this class simply inherit it privately +class NoCopy { + protected: + NoCopy(void) {} + private: + NoCopy(const NoCopy&); + NoCopy& operator=(const NoCopy&); +}; +/// @brief Internal helper class that makes all default constructors private. +/// +/// @detail This prevents initializing class making it static unless an explicit constructor is declared. +/// When using this class simply inherit it privately +class StaticClass { + private: + StaticClass(void); + StaticClass(const StaticClass&); + StaticClass& operator=(const StaticClass&); +}; +} // namespace base +/// @brief Represents enumeration for severity level used to determine level of logging +/// +/// @detail With Easylogging++, developers may disable or enable any level regardless of +/// what the severity is. Or they can choose to log using hierarchical logging flag +enum class Level : base::type::EnumType { + /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels + Global = 1, + /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. + Trace = 2, + /// @brief Informational events most useful for developers to debug application + Debug = 4, + /// @brief Severe error information that will presumably abort application + Fatal = 8, + /// @brief Information representing errors in application but application will keep running + Error = 16, + /// @brief Useful when application has potentially harmful situtaions + Warning = 32, + /// @brief Information that can be highly useful and vary with verbose logging level. + Verbose = 64, + /// @brief Mainly useful to represent current progress of application + Info = 128, + /// @brief Represents unknown level + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::Level +class LevelHelper : base::StaticClass { + public: + /// @brief Represents minimum valid level. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(Level::Trace); + /// @brief Represents maximum valid level. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(Level::Info); + /// @brief Casts level to int, useful for iterating through enum. + static base::type::EnumType castToInt(Level level) { + return static_cast<base::type::EnumType>(level); + } + /// @brief Casts int(ushort) to level, useful for iterating through enum. + static Level castFromInt(base::type::EnumType l) { + return static_cast<Level>(l); + } + /// @brief Converts level to associated const char* + /// @return Upper case string based level. + static const char* convertToString(Level level); + /// @brief Converts from prefix of levelStr to Level + /// @param levelStr Upper case string based level. + /// Lower case is also valid but providing upper case is recommended. + static Level convertFromStringPrefix(const char* levelStr); + /// @brief Converts from levelStr to Level + /// @param levelStr Upper case string based level. + /// Lower case is also valid but providing upper case is recommended. + static Level convertFromString(const char* levelStr); + /// @brief Applies specified function to each level starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed as pointer and + /// is left-shifted so this can be used inside function (fn) to represent current level. + /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. + static void forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn); +}; +/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect +/// of logging +enum class ConfigurationType : base::type::EnumType { + /// @brief Determines whether or not corresponding level and logger of logging is enabled + /// You may disable all logs by using el::Level::Global + Enabled = 1, + /// @brief Whether or not to write corresponding log to log file + ToFile = 2, + /// @brief Whether or not to write corresponding level and logger log to standard output. + /// By standard output meaning termnal, command prompt etc + ToStandardOutput = 4, + /// @brief Determines format of logging corresponding level and logger. + Format = 8, + /// @brief Determines log file (full path) to write logs to for correponding level and logger + Filename = 16, + /// @brief Specifies precision of the subsecond part. It should be within range (1-6). + SubsecondPrecision = 32, + /// @brief Alias of SubsecondPrecision (for backward compatibility) + MillisecondsWidth = SubsecondPrecision, + /// @brief Determines whether or not performance tracking is enabled. + /// + /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger + PerformanceTracking = 64, + /// @brief Specifies log file max size. + /// + /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will + /// be truncated and re-initiated. + MaxLogFileSize = 128, + /// @brief Specifies number of log entries to hold until we flush pending log data + LogFlushThreshold = 256, + /// @brief Represents unknown configuration + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::ConfigurationType +class ConfigurationTypeHelper : base::StaticClass { + public: + /// @brief Represents minimum valid configuration type. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(ConfigurationType::Enabled); + /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(ConfigurationType::MaxLogFileSize); + /// @brief Casts configuration type to int, useful for iterating through enum. + static base::type::EnumType castToInt(ConfigurationType configurationType) { + return static_cast<base::type::EnumType>(configurationType); + } + /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum. + static ConfigurationType castFromInt(base::type::EnumType c) { + return static_cast<ConfigurationType>(c); + } + /// @brief Converts configuration type to associated const char* + /// @returns Upper case string based configuration type. + static const char* convertToString(ConfigurationType configurationType); + /// @brief Converts from configStr to ConfigurationType + /// @param configStr Upper case string based configuration type. + /// Lower case is also valid but providing upper case is recommended. + static ConfigurationType convertFromString(const char* configStr); + /// @brief Applies specified function to each configuration type starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted + /// so this can be used inside function (fn) to represent current configuration type. + /// @param fn function to apply with each configuration type. + /// This bool represent whether or not to stop iterating through configurations. + static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn); +}; +/// @brief Flags used while writing logs. This flags are set by user +enum class LoggingFlag : base::type::EnumType { + /// @brief Makes sure we have new line for each container log entry + NewLineForContainer = 1, + /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose + /// logging is allowed via that module. + AllowVerboseIfModuleNotSpecified = 2, + /// @brief When handling crashes by default, detailed crash reason will be logged as well + LogDetailedCrashReason = 4, + /// @brief Allows to disable application abortion when logged using FATAL level + DisableApplicationAbortOnFatalLog = 8, + /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default + ImmediateFlush = 16, + /// @brief Enables strict file rolling + StrictLogFileSizeCheck = 32, + /// @brief Make terminal output colorful for supported terminals + ColoredTerminalOutput = 64, + /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") + MultiLoggerSupport = 128, + /// @brief Disables comparing performance tracker's checkpoints + DisablePerformanceTrackingCheckpointComparison = 256, + /// @brief Disable VModules + DisableVModules = 512, + /// @brief Disable VModules extensions + DisableVModulesExtensions = 1024, + /// @brief Enables hierarchical logging + HierarchicalLogging = 2048, + /// @brief Creates logger automatically when not available + CreateLoggerAutomatically = 4096, + /// @brief Adds spaces b/w logs that separated by left-shift operator + AutoSpacing = 8192, + /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) + FixedTimeFormat = 16384 +}; +namespace base { +/// @brief Namespace containing constants used internally. +namespace consts { +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#endif +// Level log values - These are values that are replaced in place of %level format specifier +static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO "); +static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); +static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARN "); +static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); +static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); +static const base::type::char_t* kVerboseLevelLogValue = ELPP_LITERAL("VER"); +static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); +static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); +static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); +static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); +static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); +static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); +static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); +static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); +// Format specifiers - These are used to define log format +static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); +static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); +static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); +static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); +static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); +static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); +static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); +static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); +static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); +static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); +static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); +static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); +static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); +static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); +static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); +static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; +// Date/time +static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; +static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August", + "September", "October", "November", "December" + }; +static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; +static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; +static const int kYearBase = 1900; +static const char* kAm = "AM"; +static const char* kPm = "PM"; +// Miscellaneous constants +#ifdef ELPP_DEFAULT_LOGGER +static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER; +#else +static const char* kDefaultLoggerId = "default"; +#endif +#ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER +static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; +#else +static const char* kPerformanceLoggerId = "performance"; +#endif #if defined(ELPP_SYSLOG) - static const char* kSysLogLoggerId = "syslog"; +static const char* kSysLogLoggerId = "syslog"; #endif // defined(ELPP_SYSLOG) - static const char* kNullPointer = "nullptr"; - static const char kFormatSpecifierChar = '%'; +static const char* kNullPointer = "nullptr"; +static const char kFormatSpecifierChar = '%'; #if ELPP_VARIADIC_TEMPLATES_SUPPORTED - static const char kFormatSpecifierCharValue = 'v'; +static const char kFormatSpecifierCharValue = 'v'; #endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED - static const unsigned int kMaxLogPerContainer = 100; - static const unsigned int kMaxLogPerCounter = 100000; - static const unsigned int kDefaultMillisecondsWidth = 3; - static const base::type::VerboseLevel kMaxVerboseLevel = 9; - static const char* kUnknownUser = "user"; - static const char* kUnknownHost = "unknown-host"; +static const unsigned int kMaxLogPerContainer = 100; +static const unsigned int kMaxLogPerCounter = 100000; +static const unsigned int kDefaultSubsecondPrecision = 3; +static const base::type::VerboseLevel kMaxVerboseLevel = 9; +static const char* kUnknownUser = "user"; +static const char* kUnknownHost = "unknown-host"; #if defined(ELPP_DEFAULT_LOG_FILE) - static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; +static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; #else -# if ELPP_OS_UNIX +# if ELPP_OS_UNIX # if ELPP_OS_ANDROID - static const char* kDefaultLogFile = "logs/myeasylog.log"; +static const char* kDefaultLogFile = "logs/myeasylog.log"; # else - static const char* kDefaultLogFile = "logs/myeasylog.log"; +static const char* kDefaultLogFile = "logs/myeasylog.log"; # endif // ELPP_OS_ANDROID -# elif ELPP_OS_WINDOWS - static const char* kDefaultLogFile = "logs\\myeasylog.log"; -# endif // ELPP_OS_UNIX +# elif ELPP_OS_WINDOWS +static const char* kDefaultLogFile = "logs\\myeasylog.log"; +# endif // ELPP_OS_UNIX #endif // defined(ELPP_DEFAULT_LOG_FILE) #if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) - static const char* kDefaultLogFileParam = "--default-log-file"; +static const char* kDefaultLogFileParam = "--default-log-file"; #endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) #if defined(ELPP_LOGGING_FLAGS_FROM_ARG) - static const char* kLoggingFlagsParam = "--logging-flags"; +static const char* kLoggingFlagsParam = "--logging-flags"; #endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) #if ELPP_OS_WINDOWS - static const char* kFilePathSeperator = "\\"; +static const char* kFilePathSeperator = "\\"; #else - static const char* kFilePathSeperator = "/"; +static const char* kFilePathSeperator = "/"; #endif // ELPP_OS_WINDOWS - static const char* kValidLoggerIdSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; - static const char* kConfigurationComment = "##"; - static const char* kConfigurationLevel = "*"; - static const char* kConfigurationLoggerId = "--"; - static const std::size_t kSourceFilenameMaxLength = 100; - static const std::size_t kSourceLineMaxLength = 10; - static const Level kPerformanceTrackerDefaultLevel = Level::Info; - const struct { - double value; - const base::type::char_t* unit; - } kTimeFormats[] = { - { 1000.0f, ELPP_LITERAL("mis") }, - { 1000.0f, ELPP_LITERAL("ms") }, - { 60.0f, ELPP_LITERAL("seconds") }, - { 60.0f, ELPP_LITERAL("minutes") }, - { 24.0f, ELPP_LITERAL("hours") }, - { 7.0f, ELPP_LITERAL("days") } - }; - static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); - const struct { - int numb; - const char* name; - const char* brief; - const char* detail; - } kCrashSignals[] = { - // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) - { SIGABRT, "SIGABRT", "Abnormal termination", - "Program was abnormally terminated." }, - { SIGFPE, "SIGFPE", "Erroneous arithmetic operation", - "Arithemetic operation issue such as division by zero or operation resulting in overflow." }, - { SIGILL, "SIGILL", "Illegal instruction", - "Generally due to a corruption in the code or to an attempt to execute data."}, - { SIGSEGV, "SIGSEGV", "Invalid access to memory", - "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." }, - { SIGINT, "SIGINT", "Interactive attention signal", - "Interruption generated (generally) by user or operating system." }, - }; - static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); - } // namespace consts - } // namespace base - typedef std::function<void(const char*, std::size_t)> PreRollOutCallback; - namespace base { - static inline void defaultPreRollOutCallback(const char*, std::size_t) {} - /// @brief Enum to represent timestamp unit - enum class TimestampUnit : base::type::EnumType { - Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 - }; - /// @brief Format flags used to determine specifiers that are active for performance improvements. - enum class FormatFlags : base::type::EnumType { - DateTime = 1<<1, LoggerId = 1<<2, File = 1<<3, Line = 1<<4, Location = 1<<5, Function = 1<<6, - User = 1<<7, Host = 1<<8, LogMessage = 1<<9, VerboseLevel = 1<<10, AppName = 1<<11, ThreadId = 1<<12, - Level = 1<<13, FileBase = 1<<14, LevelShort = 1<<15 - }; - /// @brief A milliseconds width class containing actual width and offset for date/time - class MillisecondsWidth { - public: - MillisecondsWidth(void) { init(base::consts::kDefaultMillisecondsWidth); } - explicit MillisecondsWidth(int width) { init(width); } - bool operator==(const MillisecondsWidth& msWidth) { return m_width == msWidth.m_width && m_offset == msWidth.m_offset; } - int m_width; unsigned int m_offset; - private: - void init(int width) { - if (width < 1 || width > 6) { - width = base::consts::kDefaultMillisecondsWidth; - } - m_width = width; - switch (m_width) { - case 3: m_offset = 1000; break; - case 4: m_offset = 100; break; - case 5: m_offset = 10; break; - case 6: m_offset = 1; break; - default: m_offset = 1000; break; - } - } - }; - /// @brief Namespace containing utility functions/static classes used internally - namespace utils { - /// @brief Deletes memory safely and points to null - template <typename T> - static inline - typename std::enable_if<std::is_pointer<T*>::value, void>::type - safeDelete(T*& pointer) { - if (pointer == nullptr) - return; - delete pointer; - pointer = nullptr; - } - /// @brief Gets value of const char* but if it is nullptr, a string nullptr is returned - static inline const char* charPtrVal(const char* pointer) { - return pointer == nullptr ? base::consts::kNullPointer : pointer; - } - /// @brief Aborts application due with user-defined status - static inline void abort(int status, const std::string& reason = std::string()) { - // Both status and reason params are there for debugging with tools like gdb etc - ELPP_UNUSED(status); - ELPP_UNUSED(reason); -#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) - // Ignore msvc critical error dialog - break instead (on debug mode) - _asm int 3 +static const char* kValidLoggerIdSymbols = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; +static const char* kConfigurationComment = "##"; +static const char* kConfigurationLevel = "*"; +static const char* kConfigurationLoggerId = "--"; +static const std::size_t kSourceFilenameMaxLength = 100; +static const std::size_t kSourceLineMaxLength = 10; +static const Level kPerformanceTrackerDefaultLevel = Level::Info; +const struct { + double value; + const base::type::char_t* unit; +} kTimeFormats[] = { + { 1000.0f, ELPP_LITERAL("us") }, + { 1000.0f, ELPP_LITERAL("ms") }, + { 60.0f, ELPP_LITERAL("seconds") }, + { 60.0f, ELPP_LITERAL("minutes") }, + { 24.0f, ELPP_LITERAL("hours") }, + { 7.0f, ELPP_LITERAL("days") } +}; +static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); +const struct { + int numb; + const char* name; + const char* brief; + const char* detail; +} kCrashSignals[] = { + // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) + { + SIGABRT, "SIGABRT", "Abnormal termination", + "Program was abnormally terminated." + }, + { + SIGFPE, "SIGFPE", "Erroneous arithmetic operation", + "Arithemetic operation issue such as division by zero or operation resulting in overflow." + }, + { + SIGILL, "SIGILL", "Illegal instruction", + "Generally due to a corruption in the code or to an attempt to execute data." + }, + { + SIGSEGV, "SIGSEGV", "Invalid access to memory", + "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." + }, + { + SIGINT, "SIGINT", "Interactive attention signal", + "Interruption generated (generally) by user or operating system." + }, +}; +static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif +} // namespace consts +} // namespace base +typedef std::function<void(const char*, std::size_t)> PreRollOutCallback; +namespace base { +static inline void defaultPreRollOutCallback(const char*, std::size_t) {} +/// @brief Enum to represent timestamp unit +enum class TimestampUnit : base::type::EnumType { + Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 +}; +/// @brief Format flags used to determine specifiers that are active for performance improvements. +enum class FormatFlags : base::type::EnumType { + DateTime = 1 << 1, + LoggerId = 1 << 2, + File = 1 << 3, + Line = 1 << 4, + Location = 1 << 5, + Function = 1 << 6, + User = 1 << 7, + Host = 1 << 8, + LogMessage = 1 << 9, + VerboseLevel = 1 << 10, + AppName = 1 << 11, + ThreadId = 1 << 12, + Level = 1 << 13, + FileBase = 1 << 14, + LevelShort = 1 << 15 +}; +/// @brief A subsecond precision class containing actual width and offset of the subsecond part +class SubsecondPrecision { + public: + SubsecondPrecision(void) { + init(base::consts::kDefaultSubsecondPrecision); + } + explicit SubsecondPrecision(int width) { + init(width); + } + bool operator==(const SubsecondPrecision& ssPrec) { + return m_width == ssPrec.m_width && m_offset == ssPrec.m_offset; + } + int m_width; + unsigned int m_offset; + private: + void init(int width); +}; +/// @brief Type alias of SubsecondPrecision +typedef SubsecondPrecision MillisecondsWidth; +/// @brief Namespace containing utility functions/static classes used internally +namespace utils { +/// @brief Deletes memory safely and points to null +template <typename T> +static +typename std::enable_if<std::is_pointer<T*>::value, void>::type +safeDelete(T*& pointer) { + if (pointer == nullptr) + return; + delete pointer; + pointer = nullptr; +} +/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation +/// Use these function as <pre>flag = bitwise::Or<MyEnum>(MyEnum::val1, flag);</pre> +namespace bitwise { +template <typename Enum> +static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { + return static_cast<base::type::EnumType>(flag) & static_cast<base::type::EnumType>(e); +} +template <typename Enum> +static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { + return static_cast<base::type::EnumType>(flag) & ~(static_cast<base::type::EnumType>(e)); +} +template <typename Enum> +static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { + return static_cast<base::type::EnumType>(flag) | static_cast<base::type::EnumType>(e); +} +} // namespace bitwise +template <typename Enum> +static inline void addFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Or<Enum>(e, *flag); +} +template <typename Enum> +static inline void removeFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Not<Enum>(e, *flag); +} +template <typename Enum> +static inline bool hasFlag(Enum e, base::type::EnumType flag) { + return base::utils::bitwise::And<Enum>(e, flag) > 0x0; +} +} // namespace utils +namespace threading { +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +namespace internal { +/// @brief A mutex wrapper for compiler that dont yet support std::recursive_mutex +class Mutex : base::NoCopy { + public: + Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m_underlyingMutex, &attr); + pthread_mutexattr_destroy(&attr); +# elif ELPP_OS_WINDOWS + InitializeCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + virtual ~Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_destroy(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + DeleteCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void lock(void) { +# if ELPP_OS_UNIX + pthread_mutex_lock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + EnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline bool try_lock(void) { +# if ELPP_OS_UNIX + return (pthread_mutex_trylock(&m_underlyingMutex) == 0); +# elif ELPP_OS_WINDOWS + return TryEnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void unlock(void) { +# if ELPP_OS_UNIX + pthread_mutex_unlock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + LeaveCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + private: +# if ELPP_OS_UNIX + pthread_mutex_t m_underlyingMutex; +# elif ELPP_OS_WINDOWS + CRITICAL_SECTION m_underlyingMutex; +# endif // ELPP_OS_UNIX +}; +/// @brief Scoped lock for compiler that dont yet support std::lock_guard +template <typename M> +class ScopedLock : base::NoCopy { + public: + explicit ScopedLock(M& mutex) { + m_mutex = &mutex; + m_mutex->lock(); + } + + virtual ~ScopedLock(void) { + m_mutex->unlock(); + } + private: + M* m_mutex; + ScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::Mutex Mutex; +typedef base::threading::internal::ScopedLock<base::threading::Mutex> ScopedLock; +# else +typedef std::recursive_mutex Mutex; +typedef std::lock_guard<base::threading::Mutex> ScopedLock; +# endif // !ELPP_USE_STD_THREADING #else - ::abort(); -#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) - } - /// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation - /// Use these function as <pre>flag = bitwise::Or<MyEnum>(MyEnum::val1, flag);</pre> - namespace bitwise { - template <typename Enum> - static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { - return static_cast<base::type::EnumType>(flag) & static_cast<base::type::EnumType>(e); - } - template <typename Enum> - static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { - return static_cast<base::type::EnumType>(flag) & ~(static_cast<base::type::EnumType>(e)); - } - template <typename Enum> - static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { - return static_cast<base::type::EnumType>(flag) | static_cast<base::type::EnumType>(e); - } - } // namespace bitwise - template <typename Enum> - static inline void addFlag(Enum e, base::type::EnumType* flag) { - *flag = base::utils::bitwise::Or<Enum>(e, *flag); - } - template <typename Enum> - static inline void removeFlag(Enum e, base::type::EnumType* flag) { - *flag = base::utils::bitwise::Not<Enum>(e, *flag); - } - template <typename Enum> - static inline bool hasFlag(Enum e, base::type::EnumType flag) { - return base::utils::bitwise::And<Enum>(e, flag) > 0x0; - } - } // namespace utils - namespace threading { +namespace internal { +/// @brief Mutex wrapper used when multi-threading is disabled. +class NoMutex : base::NoCopy { + public: + NoMutex(void) {} + inline void lock(void) {} + inline bool try_lock(void) { + return true; + } + inline void unlock(void) {} +}; +/// @brief Lock guard wrapper used when multi-threading is disabled. +template <typename Mutex> +class NoScopedLock : base::NoCopy { + public: + explicit NoScopedLock(Mutex&) { + } + virtual ~NoScopedLock(void) { + } + private: + NoScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::NoMutex Mutex; +typedef base::threading::internal::NoScopedLock<base::threading::Mutex> ScopedLock; +#endif // ELPP_THREADING_ENABLED +/// @brief Base of thread safe class, this class is inheritable-only +class ThreadSafe { + public: + virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } + virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } + virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } + protected: + ThreadSafe(void) {} + virtual ~ThreadSafe(void) {} + private: + base::threading::Mutex m_mutex; +}; + #if ELPP_THREADING_ENABLED -# if !ELPP_USE_STD_THREADING - namespace internal { - /// @brief A mutex wrapper for compiler that dont yet support std::mutex - class Mutex : base::NoCopy { - public: - Mutex(void) { -# if ELPP_OS_UNIX - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&m_underlyingMutex, &attr); - pthread_mutexattr_destroy(&attr); -# elif ELPP_OS_WINDOWS - InitializeCriticalSection(&m_underlyingMutex); -# endif // ELPP_OS_UNIX - } - - virtual ~Mutex(void) { -# if ELPP_OS_UNIX - pthread_mutex_destroy(&m_underlyingMutex); -# elif ELPP_OS_WINDOWS - DeleteCriticalSection(&m_underlyingMutex); -# endif // ELPP_OS_UNIX - } - - inline void lock(void) { -# if ELPP_OS_UNIX - pthread_mutex_lock(&m_underlyingMutex); -# elif ELPP_OS_WINDOWS - EnterCriticalSection(&m_underlyingMutex); -# endif // ELPP_OS_UNIX - } - - inline bool try_lock(void) { -# if ELPP_OS_UNIX - return (pthread_mutex_trylock(&m_underlyingMutex) == 0); -# elif ELPP_OS_WINDOWS - return TryEnterCriticalSection(&m_underlyingMutex); -# endif // ELPP_OS_UNIX - } - - inline void unlock(void) { -# if ELPP_OS_UNIX - pthread_mutex_unlock(&m_underlyingMutex); -# elif ELPP_OS_WINDOWS - LeaveCriticalSection(&m_underlyingMutex); -# endif // ELPP_OS_UNIX - } - - private: -# if ELPP_OS_UNIX - pthread_mutex_t m_underlyingMutex; -# elif ELPP_OS_WINDOWS - CRITICAL_SECTION m_underlyingMutex; -# endif // ELPP_OS_UNIX - }; - /// @brief Scoped lock for compiler that dont yet support std::lock_guard - template <typename M> - class ScopedLock : base::NoCopy { - public: - explicit ScopedLock(M& mutex) { - m_mutex = &mutex; - m_mutex->lock(); - } - - virtual ~ScopedLock(void) { - m_mutex->unlock(); - } - private: - M* m_mutex; - ScopedLock(void); - }; - } // namespace internal - /// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. - static inline std::string getCurrentThreadId(void) { - std::stringstream ss; +# if !ELPP_USE_STD_THREADING +/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. +static std::string getCurrentThreadId(void) { + std::stringstream ss; # if (ELPP_OS_WINDOWS) - ss << GetCurrentThreadId(); + ss << GetCurrentThreadId(); # endif // (ELPP_OS_WINDOWS) - return ss.str(); - } - static inline void msleep(int) { - // No implementation for non std::thread version - } - typedef base::threading::internal::Mutex Mutex; - typedef base::threading::internal::ScopedLock<base::threading::Mutex> ScopedLock; -# else - /// @brief Gets ID of currently running threading using std::this_thread::get_id() - static inline std::string getCurrentThreadId(void) { - std::stringstream ss; - ss << std::setfill(' ') << std::setw(16) << std::hex << std::this_thread::get_id(); - return ss.str(); - } - static inline void msleep(int ms) { - // Only when async logging enabled - this is because async is strict on compiler -# if ELPP_ASYNC_LOGGING -# if defined(ELPP_NO_SLEEP_FOR) - usleep(ms * 1000); -# else - std::this_thread::sleep_for(std::chrono::milliseconds(ms)); -# endif // defined(ELPP_NO_SLEEP_FOR) -# else - ELPP_UNUSED(ms); -# endif // ELPP_ASYNC_LOGGING - } - typedef std::recursive_mutex Mutex; - typedef std::lock_guard<std::recursive_mutex> ScopedLock; -# endif // !ELPP_USE_STD_THREADING + return ss.str(); +} +# else +/// @brief Gets ID of currently running threading using std::this_thread::get_id() +static std::string getCurrentThreadId(void) { + std::stringstream ss; + char prev_fill = ss.fill(' '); + auto prev_flags = ss.flags(std::ios::hex); + //ss.setf(std::ios::hex); + auto prev_width = ss.width(16); + ss << std::this_thread::get_id(); + ss.fill(prev_fill); + ss.flags(prev_flags); + ss.width(prev_width); + return ss.str(); +} +# endif // !ELPP_USE_STD_THREADING #else - namespace internal { - /// @brief Mutex wrapper used when multi-threading is disabled. - class NoMutex : base::NoCopy { - public: - NoMutex(void) {} - inline void lock(void) {} - inline bool try_lock(void) { return true; } - inline void unlock(void) {} - }; - /// @brief Lock guard wrapper used when multi-threading is disabled. - template <typename Mutex> - class NoScopedLock : base::NoCopy { - public: - explicit NoScopedLock(Mutex&) { - } - virtual ~NoScopedLock(void) { - } - private: - NoScopedLock(void); - }; - } // namespace internal - static inline std::string getCurrentThreadId(void) { - return std::string(); - } - static inline void msleep(int) { - // No custom implementation - } - typedef base::threading::internal::NoMutex Mutex; - typedef base::threading::internal::NoScopedLock<base::threading::Mutex> ScopedLock; +static inline std::string getCurrentThreadId(void) { + return std::string(); +} #endif // ELPP_THREADING_ENABLED - /// @brief Base of thread safe class, this class is inheritable-only - class ThreadSafe { - public: - virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } - virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } - virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } - protected: - ThreadSafe(void) {} - virtual ~ThreadSafe(void) {} - private: - base::threading::Mutex m_mutex; - }; - } // namespace threading - namespace utils { - class File : base::StaticClass { - public: - /// @brief Creates new out file stream for specified filename. - /// @return Pointer to newly created fstream or nullptr - static base::type::fstream_t* newFileStream(const std::string& filename) { - base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), - base::type::fstream_t::out -#if !defined(ELPP_FRESH_LOG_FILE) - | base::type::fstream_t::app -#endif - ); -#if defined(ELPP_UNICODE) - std::locale elppUnicodeLocale(""); -# if ELPP_OS_WINDOWS - std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16<wchar_t>); - elppUnicodeLocale = elppUnicodeLocaleWindows; -# endif // ELPP_OS_WINDOWS - fs->imbue(elppUnicodeLocale); -#endif // defined(ELPP_UNICODE) - if (fs->is_open()) { - fs->flush(); - } else { - base::utils::safeDelete(fs); - ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); - } - return fs; - } - - /// @brief Gets size of file provided in stream - static std::size_t getSizeOfFile(base::type::fstream_t* fs) { - if (fs == nullptr) { - return 0; - } - std::streampos currPos = fs->tellg(); - fs->seekg(0, fs->end); - std::size_t size = static_cast<std::size_t>(fs->tellg()); - fs->seekg(currPos); - return size; - } - - /// @brief Determines whether or not provided path exist in current file system - static inline bool pathExists(const char* path, bool considerFile = false) { - if (path == nullptr) { - return false; - } -#if ELPP_OS_UNIX - ELPP_UNUSED(considerFile); - struct stat st; - return (stat(path, &st) == 0); -#elif ELPP_OS_WINDOWS - DWORD fileType = GetFileAttributesA(path); - if (fileType == INVALID_FILE_ATTRIBUTES) { - return false; - } - return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); -#endif // ELPP_OS_UNIX - } - - /// @brief Creates specified path on file system - /// @param path Path to create. - static bool createPath(const std::string& path) { - if (path.empty()) { - return false; - } - if (base::utils::File::pathExists(path.c_str())) { - return true; - } - int status = -1; - - char* currPath = const_cast<char*>(path.c_str()); - std::string builtPath = std::string(); -#if ELPP_OS_UNIX - if (path[0] == '/') { - builtPath = "/"; - } - currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0); -#elif ELPP_OS_WINDOWS - // Use secure functions API - char* nextTok_ = nullptr; - currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_); - ELPP_UNUSED(nextTok_); -#endif // ELPP_OS_UNIX - while (currPath != nullptr) { - builtPath.append(currPath); - builtPath.append(base::consts::kFilePathSeperator); -#if ELPP_OS_UNIX - status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); - currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0); -#elif ELPP_OS_WINDOWS - status = _mkdir(builtPath.c_str()); - currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_); -#endif // ELPP_OS_UNIX - } - if (status == -1) { - ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); - return false; - } - return true; - } - /// @brief Extracts path of filename with leading slash - static std::string extractPathFromFilename(const std::string& fullPath, - const char* seperator = base::consts::kFilePathSeperator) { - if ((fullPath == "") || (fullPath.find(seperator) == std::string::npos)) { - return fullPath; - } - std::size_t lastSlashAt = fullPath.find_last_of(seperator); - if (lastSlashAt == 0) { - return std::string(seperator); - } - return fullPath.substr(0, lastSlashAt + 1); - } - /// @brief builds stripped filename and puts it in buff - static void buildStrippedFilename(const char* filename, char buff[], const std::string &commonPrefix = NULL, - std::size_t limit = base::consts::kSourceFilenameMaxLength) { - if (!commonPrefix.empty()) { - if (!strncmp(filename, commonPrefix.c_str(), commonPrefix.size())) - filename += commonPrefix.size(); - } - std::size_t sizeOfFilename = strlen(filename); - if (sizeOfFilename >= limit) { - filename += (sizeOfFilename - limit); - if (filename[0] != '.' && filename[1] != '.') { // prepend if not already - filename += 3; // 3 = '..' - STRCAT(buff, "..", limit); - } - } - STRCAT(buff, filename, limit); - } - /// @brief builds base filename and puts it in buff - static void buildBaseFilename(const std::string& fullPath, char buff[], - std::size_t limit = base::consts::kSourceFilenameMaxLength, - const char* seperator = base::consts::kFilePathSeperator) { - const char *filename = fullPath.c_str(); - std::size_t lastSlashAt = fullPath.find_last_of(seperator); - filename += lastSlashAt ? lastSlashAt+1 : 0; - std::size_t sizeOfFilename = strlen(filename); - if (sizeOfFilename >= limit) { - filename += (sizeOfFilename - limit); - if (filename[0] != '.' && filename[1] != '.') { // prepend if not already - filename += 3; // 3 = '..' - STRCAT(buff, "..", limit); - } - } - STRCAT(buff, filename, limit); - } - }; - /// @brief String utilities helper class used internally. You should not use it. - class Str : base::StaticClass { - public: - /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. - static inline bool isDigit(char c) { - return c >= '0' && c <= '9'; - } - - /// @brief Matches wildcards, '*' and '?' only supported. - static bool wildCardMatch(const char* str, const char* pattern) { - while (*pattern) { - switch (*pattern) { - case '?': - if (!*str) - return false; - ++str; - ++pattern; - break; - case '*': - if (wildCardMatch(str, pattern + 1)) - return true; - if (*str && wildCardMatch(str + 1, pattern)) - return true; - return false; - default: - if (*str++ != *pattern++) - return false; - break; - } - } - return !*str && !*pattern; - } - - /// @brief Trims string from start - /// @param [in,out] str String to trim - static inline std::string& ltrim(std::string& str) { - str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun<int, int>(&std::isspace)))); - return str; - } - - /// @brief Trim string from end - /// @param [in,out] str String to trim - static inline std::string& rtrim(std::string& str) { - str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun<int, int>(&std::isspace))).base(), str.end()); - return str; - } - - /// @brief Trims string from left and right - /// @param [in,out] str String to trim - static inline std::string& trim(std::string& str) { - return ltrim(rtrim(str)); - } - - /// @brief Determines whether or not str starts with specified string - /// @param str String to check - /// @param start String to check against - /// @return Returns true if starts with specified string, false otherwise - static inline bool startsWith(const std::string& str, const std::string& start) { - return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); - } - - /// @brief Determines whether or not str ends with specified string - /// @param str String to check - /// @param end String to check against - /// @return Returns true if ends with specified string, false otherwise - static inline bool endsWith(const std::string& str, const std::string& end) { - return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); - } - - /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. - /// @param [in,out] str String to replace from - /// @param replaceWhat Character to replace - /// @param replaceWith Character to replace with - /// @return Modified version of str - static inline std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith) { - std::replace(str.begin(), str.end(), replaceWhat, replaceWith); - return str; - } - - /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place - /// @param str String to replace from - /// @param replaceWhat Character to replace - /// @param replaceWith Character to replace with - /// @return Modified (original) str - static inline std::string& replaceAll(std::string& str, const std::string& replaceWhat, // NOLINT - const std::string& replaceWith) { - if (replaceWhat == replaceWith) - return str; - std::size_t foundAt = std::string::npos; - while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { - str.replace(foundAt, replaceWhat.length(), replaceWith); - } - return str; - } - - static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT - const base::type::string_t& replaceWith) { - std::size_t foundAt = base::type::string_t::npos; - while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { - if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { - str.erase(foundAt > 0 ? foundAt - 1 : 0, 1); - ++foundAt; - } else { - str.replace(foundAt, replaceWhat.length(), replaceWith); - return; - } - } - } +} // namespace threading +namespace utils { +class File : base::StaticClass { + public: + /// @brief Creates new out file stream for specified filename. + /// @return Pointer to newly created fstream or nullptr + static base::type::fstream_t* newFileStream(const std::string& filename); + + /// @brief Gets size of file provided in stream + static std::size_t getSizeOfFile(base::type::fstream_t* fs); + + /// @brief Determines whether or not provided path exist in current file system + static bool pathExists(const char* path, bool considerFile = false); + + /// @brief Creates specified path on file system + /// @param path Path to create. + static bool createPath(const std::string& path); + /// @brief Extracts path of filename with leading slash + static std::string extractPathFromFilename(const std::string& fullPath, + const char* seperator = base::consts::kFilePathSeperator); + /// @brief builds stripped filename and puts it in buff + static void buildStrippedFilename(const char* filename, char buff[], const std::string &commonPrefix = NULL, + std::size_t limit = base::consts::kSourceFilenameMaxLength); + /// @brief builds base filename and puts it in buff + static void buildBaseFilename(const std::string& fullPath, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength, + const char* seperator = base::consts::kFilePathSeperator); +}; +/// @brief String utilities helper class used internally. You should not use it. +class Str : base::StaticClass { + public: + /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. + static inline bool isDigit(char c) { + return c >= '0' && c <= '9'; + } + + /// @brief Matches wildcards, '*' and '?' only supported. + static bool wildCardMatch(const char* str, const char* pattern); + + static std::string& ltrim(std::string& str); + static std::string& rtrim(std::string& str); + static std::string& trim(std::string& str); + + /// @brief Determines whether or not str starts with specified string + /// @param str String to check + /// @param start String to check against + /// @return Returns true if starts with specified string, false otherwise + static bool startsWith(const std::string& str, const std::string& start); + + /// @brief Determines whether or not str ends with specified string + /// @param str String to check + /// @param end String to check against + /// @return Returns true if ends with specified string, false otherwise + static bool endsWith(const std::string& str, const std::string& end); + + /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. + /// @param [in,out] str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified version of str + static std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith); + + /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place + /// @param str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified (original) str + static std::string& replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith); + + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith); #if defined(ELPP_UNICODE) - static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT - const std::string& replaceWith) { - replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); - } + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith); #endif // defined(ELPP_UNICODE) - /// @brief Converts string to uppercase - /// @param str String to convert - /// @return Uppercase string - static inline std::string& toUpper(std::string& str) { - std::transform(str.begin(), str.end(), str.begin(), ::toupper); - return str; - } - - /// @brief Compares cstring equality - uses strcmp - static inline bool cStringEq(const char* s1, const char* s2) { - if (s1 == nullptr && s2 == nullptr) return true; - if (s1 == nullptr || s2 == nullptr) return false; - return strcmp(s1, s2) == 0; - } - - /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) - /// Dont use strcasecmp because of CRT (VC++) - static bool cStringCaseEq(const char* s1, const char* s2) { - if (s1 == nullptr && s2 == nullptr) return true; - if (s1 == nullptr || s2 == nullptr) return false; - if (strlen(s1) != strlen(s2)) return false; - while (*s1 != '\0' && *s2 != '\0') { - if (::toupper(*s1) != ::toupper(*s2)) return false; - ++s1; - ++s2; - } - return true; - } - - /// @brief Returns true if c exist in str - static inline bool contains(const char* str, char c) { - for (; *str; ++str) { - if (*str == c) - return true; - } - return false; - } - - static inline char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true) { - char localBuff[10] = ""; - char* p = localBuff + sizeof(localBuff) - 2; - if (n > 0) { - for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) - *--p = static_cast<char>(n % 10 + '0'); - } else { - *--p = '0'; - --len; - } - if (zeroPadded) - while (p > localBuff && len-- > 0) *--p = static_cast<char>('0'); - return addToBuff(p, buf, bufLim); - } - - static inline char* addToBuff(const char* str, char* buf, const char* bufLim) { - while ((buf < bufLim) && ((*buf = *str++) != '\0')) - ++buf; - return buf; - } - - static inline char* clearBuff(char buff[], std::size_t lim) { - STRCPY(buff, "", lim); - ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro - return buff; - } - - /// @brief Converst wchar* to char* - /// NOTE: Need to free return value after use! - static char* wcharPtrToCharPtr(const wchar_t* line) { - std::size_t len_ = wcslen(line) + 1; - char* buff_ = static_cast<char*>(malloc(len_ + 1)); -# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) - std::wcstombs(buff_, line, len_); -# elif ELPP_OS_WINDOWS - std::size_t convCount_ = 0; - mbstate_t mbState_; - ::memset(static_cast<void*>(&mbState_), 0, sizeof(mbState_)); - wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); -# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) - return buff_; - } - }; - /// @brief Operating System helper static class used internally. You should not use it. - class OS : base::StaticClass { - public: + /// @brief Converts string to uppercase + /// @param str String to convert + /// @return Uppercase string + static std::string& toUpper(std::string& str); + + /// @brief Compares cstring equality - uses strcmp + static bool cStringEq(const char* s1, const char* s2); + + /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) + /// Dont use strcasecmp because of CRT (VC++) + static bool cStringCaseEq(const char* s1, const char* s2); + + /// @brief Returns true if c exist in str + static bool contains(const char* str, char c); + + static char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true); + static char* addToBuff(const char* str, char* buf, const char* bufLim); + static char* clearBuff(char buff[], std::size_t lim); + + /// @brief Converst wchar* to char* + /// NOTE: Need to free return value after use! + static char* wcharPtrToCharPtr(const wchar_t* line); +}; +/// @brief Operating System helper static class used internally. You should not use it. +class OS : base::StaticClass { + public: #if ELPP_OS_WINDOWS - /// @brief Gets environment variables for Windows based OS. - /// We are not using <code>getenv(const char*)</code> because of CRT deprecation - /// @param varname Variable name to get environment variable value for - /// @return If variable exist the value of it otherwise nullptr - static const char* getWindowsEnvironmentVariable(const char* varname) { - const DWORD bufferLen = 50; - static char buffer[bufferLen]; - if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { - return buffer; - } - return nullptr; - } + /// @brief Gets environment variables for Windows based OS. + /// We are not using <code>getenv(const char*)</code> because of CRT deprecation + /// @param varname Variable name to get environment variable value for + /// @return If variable exist the value of it otherwise nullptr + static const char* getWindowsEnvironmentVariable(const char* varname); #endif // ELPP_OS_WINDOWS #if ELPP_OS_ANDROID - /// @brief Reads android property value - static inline std::string getProperty(const char* prop) { - char propVal[PROP_VALUE_MAX + 1]; - int ret = __system_property_get(prop, propVal); - return ret == 0 ? std::string() : std::string(propVal); - } - - /// @brief Reads android device name - static std::string getDeviceName(void) { - std::stringstream ss; - std::string manufacturer = getProperty("ro.product.manufacturer"); - std::string model = getProperty("ro.product.model"); - if (manufacturer.empty() || model.empty()) { - return std::string(); - } - ss << manufacturer << "-" << model; - return ss.str(); - } + /// @brief Reads android property value + static std::string getProperty(const char* prop); + + /// @brief Reads android device name + static std::string getDeviceName(void); #endif // ELPP_OS_ANDROID - - /// @brief Runs command on terminal and returns the output. - /// - /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. - /// @param command Bash command - /// @return Result of bash output or empty string if no result found. - static const std::string getBashOutput(const char* command) { -#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) - if (command == nullptr) { - return std::string(); - } - FILE* proc = nullptr; - if ((proc = popen(command, "r")) == nullptr) { - ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); - return std::string(); - } - char hBuff[4096]; - if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { - pclose(proc); - if (hBuff[strlen(hBuff) - 1] == '\n') { - hBuff[strlen(hBuff) - 1] = '\0'; - } - return std::string(hBuff); - } - return std::string(); -#else - ELPP_UNUSED(command); - return std::string(); -#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) - } - - /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) - /// @param variableName Environment variable name - /// @param defaultVal If no environment variable or value found the value to return by default - /// @param alternativeBashCommand If environment variable not found what would be alternative bash command - /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' - static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, const char* alternativeBashCommand = nullptr) { -#if ELPP_OS_UNIX - const char* val = getenv(variableName); -#elif ELPP_OS_WINDOWS - const char* val = getWindowsEnvironmentVariable(variableName); -#endif // ELPP_OS_UNIX - if ((val == nullptr) || ((strcmp(val, "") == 0))) { -#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) - // Try harder on unix-based systems - std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); - if (valBash.empty()) { - return std::string(defaultVal); - } else { - return valBash; - } -#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX - ELPP_UNUSED(alternativeBashCommand); - return std::string(defaultVal); -#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) - } - return std::string(val); - } - /// @brief Gets current username. - static inline std::string currentUser(void) { -#if ELPP_OS_UNIX && !ELPP_OS_ANDROID - return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); -#elif ELPP_OS_WINDOWS - return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); -#elif ELPP_OS_ANDROID - ELPP_UNUSED(base::consts::kUnknownUser); - return std::string("android"); -#else - return std::string(); -#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID - } - - /// @brief Gets current host name or computer name. - /// - /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen - static inline std::string currentHost(void) { -#if ELPP_OS_UNIX && !ELPP_OS_ANDROID - return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); -#elif ELPP_OS_WINDOWS - return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); -#elif ELPP_OS_ANDROID - ELPP_UNUSED(base::consts::kUnknownHost); - return getDeviceName(); -#else - return std::string(); -#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID - } - /// @brief Whether or not terminal supports colors - static inline bool termSupportsColor(void) { - std::string term = getEnvironmentVariable("TERM", ""); - return term == "xterm" || term == "xterm-color" || term == "xterm-256color" - || term == "screen" || term == "linux" || term == "cygwin" - || term == "screen-256color"; - } - }; - extern std::string s_currentUser; - extern std::string s_currentHost; - extern bool s_termSupportsColor; -#define ELPP_INITI_BASIC_DECLR \ -namespace el {\ -namespace base {\ -namespace utils {\ -std::string s_currentUser = el::base::utils::OS::currentUser(); \ -std::string s_currentHost = el::base::utils::OS::currentHost(); \ -bool s_termSupportsColor = el::base::utils::OS::termSupportsColor(); \ -}\ -}\ -} - /// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str - class DateTime : base::StaticClass { - public: - /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current millisecond. - /// - /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided - /// @param [in,out] tv Pointer that gets updated - static void gettimeofday(struct timeval* tv) { -#if ELPP_OS_WINDOWS - if (tv != nullptr) { -# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) - const unsigned __int64 delta_ = 11644473600000000Ui64; -# else - const unsigned __int64 delta_ = 11644473600000000ULL; -# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) - const double secOffSet = 0.000001; - const unsigned long usecOffSet = 1000000; - FILETIME fileTime; - GetSystemTimeAsFileTime(&fileTime); - unsigned __int64 present = 0; - present |= fileTime.dwHighDateTime; - present = present << 32; - present |= fileTime.dwLowDateTime; - present /= 10; // mic-sec - // Subtract the difference - present -= delta_; - tv->tv_sec = static_cast<long>(present * secOffSet); - tv->tv_usec = static_cast<long>(present % usecOffSet); - } -#else - ::gettimeofday(tv, nullptr); -#endif // ELPP_OS_WINDOWS - } - - /// @brief Gets current date and time with milliseconds. - /// @param format User provided date/time format - /// @param msWidth A pointer to base::MillisecondsWidth from configuration (non-null) - /// @returns string based date time in specified format. - static inline std::string getDateTime(const char* format, const base::MillisecondsWidth* msWidth) { - struct timeval currTime; - gettimeofday(&currTime); - struct ::tm timeInfo; - buildTimeInfo(&currTime, &timeInfo); - const int kBuffSize = 30; - char buff_[kBuffSize] = ""; - parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(currTime.tv_usec / msWidth->m_offset), msWidth); - return std::string(buff_); - } - - /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc - static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { - double result = static_cast<double>(time); - base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit); - const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; - for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { - if (result <= base::consts::kTimeFormats[i].value) { - break; - } - result /= base::consts::kTimeFormats[i].value; - unit = base::consts::kTimeFormats[i + 1].unit; - } - base::type::stringstream_t ss; - ss << result << " " << unit; - return ss.str(); - } - - /// @brief Gets time difference in milli/micro second depending on timestampUnit - static inline unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, base::TimestampUnit timestampUnit) { - if (timestampUnit == base::TimestampUnit::Microsecond) { - return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) - - static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec)); - } else { - return static_cast<unsigned long long>((((endTime.tv_sec - startTime.tv_sec) * 1000000) + (endTime.tv_usec - startTime.tv_usec)) / 1000); - } - } - - private: - static inline struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { -#if ELPP_OS_UNIX - time_t rawTime = currTime->tv_sec; - ::localtime_r(&rawTime, timeInfo); - return timeInfo; -#else -# if ELPP_COMPILER_MSVC - ELPP_UNUSED(currTime); - time_t t; - _time64(&t); - localtime_s(timeInfo, &t); - return timeInfo; -# else - // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method - time_t rawTime = currTime->tv_sec; - struct tm* tmInf = localtime(&rawTime); - *timeInfo = *tmInf; - return timeInfo; -# endif // ELPP_COMPILER_MSVC -#endif // ELPP_OS_UNIX - } - static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, - std::size_t msec, const base::MillisecondsWidth* msWidth) { - const char* bufLim = buf + bufSz; - for (; *format; ++format) { - if (*format == base::consts::kFormatSpecifierChar) { - switch (*++format) { - case base::consts::kFormatSpecifierChar: // Escape - break; - case '\0': // End - --format; - break; - case 'd': // Day - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); - continue; - case 'a': // Day of week (short) - buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); - continue; - case 'A': // Day of week (long) - buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); - continue; - case 'M': // month - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); - continue; - case 'b': // month (short) - buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); - continue; - case 'B': // month (long) - buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); - continue; - case 'y': // year (two digits) - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); - continue; - case 'Y': // year (four digits) - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); - continue; - case 'h': // hour (12-hour) - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); - continue; - case 'H': // hour (24-hour) - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); - continue; - case 'm': // minute - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); - continue; - case 's': // second - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); - continue; - case 'z': // milliseconds - case 'g': - buf = base::utils::Str::convertAndAddToBuff(msec, msWidth->m_width, buf, bufLim); - continue; - case 'F': // AM/PM - buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim); - continue; - default: - continue; - } - } - if (buf == bufLim) break; - *buf++ = *format; - } - return buf; - } - }; - /// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) - class CommandLineArgs { - public: - CommandLineArgs(void) { - setArgs(0, static_cast<char**>(nullptr)); - } - CommandLineArgs(int argc, const char** argv) { - setArgs(argc, argv); - } - CommandLineArgs(int argc, char** argv) { - setArgs(argc, argv); - } - virtual ~CommandLineArgs(void) {} - /// @brief Sets arguments and parses them - inline void setArgs(int argc, const char** argv) { - setArgs(argc, const_cast<char**>(argv)); - } - /// @brief Sets arguments and parses them - inline void setArgs(int argc, char** argv) { - m_params.clear(); - m_paramsWithValue.clear(); - if (argc == 0 || argv == nullptr) { - return; - } - m_argc = argc; - m_argv = argv; - for (int i = 1; i < m_argc; ++i) { - const char* v = (strstr(m_argv[i], "=")); - if (v != nullptr && strlen(v) > 0) { - std::string key = std::string(m_argv[i]); - key = key.substr(0, key.find_first_of('=')); - if (hasParamWithValue(key.c_str())) { - ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" - << getParamValue(key.c_str()) << "]"); - } else { - m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1))); - } - } - if (v == nullptr) { - if (hasParam(m_argv[i])) { - ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists"); - } else { - m_params.push_back(std::string(m_argv[i])); - } - } - } - } - /// @brief Returns true if arguments contain paramKey with a value (seperated by '=') - inline bool hasParamWithValue(const char* paramKey) const { - return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); - } - /// @brief Returns value of arguments - /// @see hasParamWithValue(const char*) - inline const char* getParamValue(const char* paramKey) const { - return m_paramsWithValue.find(std::string(paramKey))->second.c_str(); - } - /// @brief Return true if arguments has a param (not having a value) i,e without '=' - inline bool hasParam(const char* paramKey) const { - return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); - } - /// @brief Returns true if no params available. This exclude argv[0] - inline bool empty(void) const { - return m_params.empty() && m_paramsWithValue.empty(); - } - /// @brief Returns total number of arguments. This exclude argv[0] - inline std::size_t size(void) const { - return m_params.size() + m_paramsWithValue.size(); - } - inline friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) { - for (int i = 1; i < c.m_argc; ++i) { - os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]"); - if (i < c.m_argc - 1) { - os << ELPP_LITERAL(" "); - } - } - return os; - } - - private: - int m_argc; - char** m_argv; - std::map<std::string, std::string> m_paramsWithValue; - std::vector<std::string> m_params; - }; - /// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. - /// - /// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement - /// unregisterAll() and deepCopy(const AbstractRegistry<T_Ptr, Container>&) and write registerNew() method according to container - /// and few more methods; get() to find element, unregister() to unregister single entry. - /// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. - template <typename T_Ptr, typename Container> - class AbstractRegistry : public base::threading::ThreadSafe { - public: - typedef typename Container::iterator iterator; - typedef typename Container::const_iterator const_iterator; - - /// @brief Default constructor - AbstractRegistry(void) {} - - /// @brief Move constructor that is useful for base classes - AbstractRegistry(AbstractRegistry&& sr) { - if (this == &sr) { - return; - } - unregisterAll(); - m_list = std::move(sr.m_list); - } - - bool operator==(const AbstractRegistry<T_Ptr, Container>& other) { - if (size() != other.size()) { - return false; - } - for (std::size_t i = 0; i < m_list.size(); ++i) { - if (m_list.at(i) != other.m_list.at(i)) { - return false; - } - } - return true; - } - - bool operator!=(const AbstractRegistry<T_Ptr, Container>& other) { - if (size() != other.size()) { - return true; - } - for (std::size_t i = 0; i < m_list.size(); ++i) { - if (m_list.at(i) != other.m_list.at(i)) { - return true; - } - } - return false; - } - - /// @brief Assignment move operator - AbstractRegistry& operator=(AbstractRegistry&& sr) { - if (this == &sr) { - return *this; - } - unregisterAll(); - m_list = std::move(sr.m_list); - return *this; - } - - virtual ~AbstractRegistry(void) { - } - - /// @return Iterator pointer from start of repository - virtual inline iterator begin(void) ELPP_FINAL { - return m_list.begin(); - } - - /// @return Iterator pointer from end of repository - virtual inline iterator end(void) ELPP_FINAL { - return m_list.end(); - } - - - /// @return Constant iterator pointer from start of repository - virtual inline const_iterator cbegin(void) const ELPP_FINAL { - return m_list.cbegin(); - } - - /// @return End of repository - virtual inline const_iterator cend(void) const ELPP_FINAL { - return m_list.cend(); - } - - /// @return Whether or not repository is empty - virtual inline bool empty(void) const ELPP_FINAL { - return m_list.empty(); - } - - /// @return Size of repository - virtual inline std::size_t size(void) const ELPP_FINAL { - return m_list.size(); - } - - /// @brief Returns underlying container by reference - virtual inline Container& list(void) ELPP_FINAL { - return m_list; - } - - /// @brief Returns underlying container by constant reference. - virtual inline const Container& list(void) const ELPP_FINAL { - return m_list; + + /// @brief Runs command on terminal and returns the output. + /// + /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. + /// @param command Bash command + /// @return Result of bash output or empty string if no result found. + static const std::string getBashOutput(const char* command); + + /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) + /// @param variableName Environment variable name + /// @param defaultVal If no environment variable or value found the value to return by default + /// @param alternativeBashCommand If environment variable not found what would be alternative bash command + /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' + static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand = nullptr); + /// @brief Gets current username. + static std::string currentUser(void); + + /// @brief Gets current host name or computer name. + /// + /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen + static std::string currentHost(void); + /// @brief Whether or not terminal supports colors + static bool termSupportsColor(void); +}; +/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str +class DateTime : base::StaticClass { + public: + /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current microsecond. + /// + /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided + /// @param [in,out] tv Pointer that gets updated + static void gettimeofday(struct timeval* tv); + + /// @brief Gets current date and time with a subsecond part. + /// @param format User provided date/time format + /// @param ssPrec A pointer to base::SubsecondPrecision from configuration (non-null) + /// @returns string based date time in specified format. + static std::string getDateTime(const char* format, const base::SubsecondPrecision* ssPrec); + + /// @brief Converts timeval (struct from ctime) to string using specified format and subsecond precision + static std::string timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec); + + /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc + static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit); + + /// @brief Gets time difference in milli/micro second depending on timestampUnit + static unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit); + + + private: + static struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo); + static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec); +}; +/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) +class CommandLineArgs { + public: + CommandLineArgs(void) { + setArgs(0, static_cast<char**>(nullptr)); + } + CommandLineArgs(int argc, const char** argv) { + setArgs(argc, argv); + } + CommandLineArgs(int argc, char** argv) { + setArgs(argc, argv); + } + virtual ~CommandLineArgs(void) {} + /// @brief Sets arguments and parses them + inline void setArgs(int argc, const char** argv) { + setArgs(argc, const_cast<char**>(argv)); + } + /// @brief Sets arguments and parses them + void setArgs(int argc, char** argv); + /// @brief Returns true if arguments contain paramKey with a value (seperated by '=') + bool hasParamWithValue(const char* paramKey) const; + /// @brief Returns value of arguments + /// @see hasParamWithValue(const char*) + const char* getParamValue(const char* paramKey) const; + /// @brief Return true if arguments has a param (not having a value) i,e without '=' + bool hasParam(const char* paramKey) const; + /// @brief Returns true if no params available. This exclude argv[0] + bool empty(void) const; + /// @brief Returns total number of arguments. This exclude argv[0] + std::size_t size(void) const; + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c); + + private: + int m_argc; + char** m_argv; + std::map<std::string, std::string> m_paramsWithValue; + std::vector<std::string> m_params; +}; +/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. +/// +/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement +/// unregisterAll() and deepCopy(const AbstractRegistry<T_Ptr, Container>&) and write registerNew() method according to container +/// and few more methods; get() to find element, unregister() to unregister single entry. +/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. +template <typename T_Ptr, typename Container> +class AbstractRegistry : public base::threading::ThreadSafe { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + /// @brief Default constructor + AbstractRegistry(void) {} + + /// @brief Move constructor that is useful for base classes + AbstractRegistry(AbstractRegistry&& sr) { + if (this == &sr) { + return; + } + unregisterAll(); + m_list = std::move(sr.m_list); + } + + bool operator==(const AbstractRegistry<T_Ptr, Container>& other) { + if (size() != other.size()) { + return false; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return false; + } + } + return true; + } + + bool operator!=(const AbstractRegistry<T_Ptr, Container>& other) { + if (size() != other.size()) { + return true; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return true; + } } - - /// @brief Unregisters all the pointers from current repository. - virtual void unregisterAll(void) = 0; - -protected: - virtual void deepCopy(const AbstractRegistry<T_Ptr, Container>&) = 0; - void reinitDeepCopy(const AbstractRegistry<T_Ptr, Container>& sr) { - unregisterAll(); - deepCopy(sr); + return false; + } + + /// @brief Assignment move operator + AbstractRegistry& operator=(AbstractRegistry&& sr) { + if (this == &sr) { + return *this; } - -private: - Container m_list; + unregisterAll(); + m_list = std::move(sr.m_list); + return *this; + } + + virtual ~AbstractRegistry(void) { + } + + /// @return Iterator pointer from start of repository + virtual inline iterator begin(void) ELPP_FINAL { + return m_list.begin(); + } + + /// @return Iterator pointer from end of repository + virtual inline iterator end(void) ELPP_FINAL { + return m_list.end(); + } + + + /// @return Constant iterator pointer from start of repository + virtual inline const_iterator cbegin(void) const ELPP_FINAL { + return m_list.cbegin(); + } + + /// @return End of repository + virtual inline const_iterator cend(void) const ELPP_FINAL { + return m_list.cend(); + } + + /// @return Whether or not repository is empty + virtual inline bool empty(void) const ELPP_FINAL { + return m_list.empty(); + } + + /// @return Size of repository + virtual inline std::size_t size(void) const ELPP_FINAL { + return m_list.size(); + } + + /// @brief Returns underlying container by reference + virtual inline Container& list(void) ELPP_FINAL { + return m_list; + } + + /// @brief Returns underlying container by constant reference. + virtual inline const Container& list(void) const ELPP_FINAL { + return m_list; + } + + /// @brief Unregisters all the pointers from current repository. + virtual void unregisterAll(void) = 0; + + protected: + virtual void deepCopy(const AbstractRegistry<T_Ptr, Container>&) = 0; + void reinitDeepCopy(const AbstractRegistry<T_Ptr, Container>& sr) { + unregisterAll(); + deepCopy(sr); + } + + private: + Container m_list; }; /// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version) @@ -2007,74 +1422,74 @@ private: /// explicitly (by using lock functions) template <typename T_Ptr, typename T_Key = const char*> class Registry : public AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>> { -public: - typedef typename Registry<T_Ptr, T_Key>::iterator iterator; - typedef typename Registry<T_Ptr, T_Key>::const_iterator const_iterator; - - Registry(void) {} - - /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. - Registry(const Registry& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() { - if (this == &sr) { - return; - } - this->reinitDeepCopy(sr); - } - - /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element - /// @see unregisterAll() - /// @see deepCopy(const AbstractRegistry&) - Registry& operator=(const Registry& sr) { - if (this == &sr) { - return *this; - } - this->reinitDeepCopy(sr); - return *this; + public: + typedef typename Registry<T_Ptr, T_Key>::iterator iterator; + typedef typename Registry<T_Ptr, T_Key>::const_iterator const_iterator; + + Registry(void) {} + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + Registry(const Registry& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() { + if (this == &sr) { + return; } - - virtual ~Registry(void) { - unregisterAll(); + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + Registry& operator=(const Registry& sr) { + if (this == &sr) { + return *this; } - -protected: - virtual inline void unregisterAll(void) ELPP_FINAL { + this->reinitDeepCopy(sr); + return *this; + } + + virtual ~Registry(void) { + unregisterAll(); + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { if (!this->empty()) { - for (auto&& curr : this->list()) { - base::utils::safeDelete(curr.second); - } - this->list().clear(); + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr.second); + } + this->list().clear(); } -} + } /// @brief Registers new registry to repository. -virtual inline void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { -unregister(uniqKey); -this->list().insert(std::make_pair(uniqKey, ptr)); -} + virtual void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { + unregister(uniqKey); + this->list().insert(std::make_pair(uniqKey, ptr)); + } /// @brief Unregisters single entry mapped to specified unique key -inline void unregister(const T_Key& uniqKey) { + void unregister(const T_Key& uniqKey) { T_Ptr* existing = get(uniqKey); if (existing != nullptr) { - base::utils::safeDelete(existing); - this->list().erase(uniqKey); + base::utils::safeDelete(existing); + this->list().erase(uniqKey); } -} + } /// @brief Gets pointer from repository. If none found, nullptr is returned. -inline T_Ptr* get(const T_Key& uniqKey) { + T_Ptr* get(const T_Key& uniqKey) { iterator it = this->list().find(uniqKey); return it == this->list().end() - ? nullptr - : it->second; -} + ? nullptr + : it->second; + } -private: -virtual inline void deepCopy(const AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>>& sr) ELPP_FINAL { -for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { - registerNew(it->first, new T_Ptr(*it->second)); -} -} + private: + virtual void deepCopy(const AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>>& sr) ELPP_FINAL { + for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { + registerNew(it->first, new T_Ptr(*it->second)); + } + } }; /// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version) @@ -2083,348 +1498,222 @@ for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { /// should be made thread-safe explicitly template <typename T_Ptr, typename Pred> class RegistryWithPred : public AbstractRegistry<T_Ptr, std::vector<T_Ptr*>> { -public: - typedef typename RegistryWithPred<T_Ptr, Pred>::iterator iterator; - typedef typename RegistryWithPred<T_Ptr, Pred>::const_iterator const_iterator; - - RegistryWithPred(void) { - } - - virtual ~RegistryWithPred(void) { - unregisterAll(); - } - - /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. - RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() { - if (this == &sr) { - return; - } - this->reinitDeepCopy(sr); + public: + typedef typename RegistryWithPred<T_Ptr, Pred>::iterator iterator; + typedef typename RegistryWithPred<T_Ptr, Pred>::const_iterator const_iterator; + + RegistryWithPred(void) { + } + + virtual ~RegistryWithPred(void) { + unregisterAll(); + } + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() { + if (this == &sr) { + return; } - - /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element - /// @see unregisterAll() - /// @see deepCopy(const AbstractRegistry&) - RegistryWithPred& operator=(const RegistryWithPred& sr) { - if (this == &sr) { - return *this; - } - this->reinitDeepCopy(sr); - return *this; + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + RegistryWithPred& operator=(const RegistryWithPred& sr) { + if (this == &sr) { + return *this; } - - friend inline base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { - for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { - os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); - } - return os; + this->reinitDeepCopy(sr); + return *this; + } + + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); } - -protected: - virtual inline void unregisterAll(void) ELPP_FINAL { + return os; + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { if (!this->empty()) { - for (auto&& curr : this->list()) { - base::utils::safeDelete(curr); - } - this->list().clear(); + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr); + } + this->list().clear(); } -} + } -virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { -if (ptr) { - iterator iter = this->begin(); - for (; iter != this->end(); ++iter) { + virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { + if (ptr) { + iterator iter = this->begin(); + for (; iter != this->end(); ++iter) { if (ptr == *iter) { - break; + break; } - } - if (iter != this->end() && *iter != nullptr) { + } + if (iter != this->end() && *iter != nullptr) { this->list().erase(iter); base::utils::safeDelete(*iter); + } } -} -} + } -virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { -this->list().push_back(ptr); -} + virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { + this->list().push_back(ptr); + } /// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate /// in order to validate pointer. -template <typename T, typename T2> -inline T_Ptr* get(const T& arg1, const T2 arg2) { + template <typename T, typename T2> + T_Ptr* get(const T& arg1, const T2 arg2) { iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2)); if (iter != this->list().end() && *iter != nullptr) { - return *iter; + return *iter; } return nullptr; -} + } -private: -virtual inline void deepCopy(const AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>& sr) { + private: + virtual void deepCopy(const AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>& sr) { for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { - registerNew(new T_Ptr(**it)); + registerNew(new T_Ptr(**it)); } -} + } }; +class Utils { + public: + template <typename T, typename TPtr> + static bool installCallback(const std::string& id, std::map<std::string, TPtr>* mapT) { + if (mapT->find(id) == mapT->end()) { + mapT->insert(std::make_pair(id, TPtr(new T()))); + return true; + } + return false; + } + template <typename T, typename TPtr> + static void uninstallCallback(const std::string& id, std::map<std::string, TPtr>* mapT) { + if (mapT->find(id) != mapT->end()) { + mapT->erase(id); + } + } + + template <typename T, typename TPtr> + static T* callback(const std::string& id, std::map<std::string, TPtr>* mapT) { + typename std::map<std::string, TPtr>::iterator iter = mapT->find(id); + if (iter != mapT->end()) { + return static_cast<T*>(iter->second.get()); + } + return nullptr; + } +}; } // namespace utils } // namespace base /// @brief Base of Easylogging++ friendly class /// /// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const` class Loggable { -public: - virtual ~Loggable(void) {} - virtual void log(el::base::type::ostream_t&) const = 0; -private: - friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { - loggable.log(os); - return os; - } + public: + virtual ~Loggable(void) {} + virtual void log(el::base::type::ostream_t&) const = 0; + private: + friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { + loggable.log(os); + return os; + } }; namespace base { - /// @brief Represents log format containing flags and date format. This is used internally to start initial log - class LogFormat : public Loggable { - public: - LogFormat(void) : - m_level(Level::Unknown), - m_userFormat(base::type::string_t()), - m_format(base::type::string_t()), - m_dateTimeFormat(std::string()), - m_flags(0x0) { - } - - LogFormat(Level level, const base::type::string_t& format) - : m_level(level), m_userFormat(format) { - parseFromFormat(m_userFormat); - } - - LogFormat(const LogFormat& logFormat) { - m_level = logFormat.m_level; - m_userFormat = logFormat.m_userFormat; - m_format = logFormat.m_format; - m_dateTimeFormat = logFormat.m_dateTimeFormat; - m_flags = logFormat.m_flags; - } - - LogFormat(LogFormat&& logFormat) { - m_level = std::move(logFormat.m_level); - m_userFormat = std::move(logFormat.m_userFormat); - m_format = std::move(logFormat.m_format); - m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat); - m_flags = std::move(logFormat.m_flags); - } - - LogFormat& operator=(const LogFormat& logFormat) { - m_level = logFormat.m_level; - m_userFormat = logFormat.m_userFormat; - m_dateTimeFormat = logFormat.m_dateTimeFormat; - m_flags = logFormat.m_flags; - return *this; - } - - virtual ~LogFormat(void) { - } - - inline bool operator==(const LogFormat& other) { - return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format && - m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags; - } - - /// @brief Updates format to be used while logging. - /// @param userFormat User provided format - void parseFromFormat(const base::type::string_t& userFormat) { - // We make copy because we will be changing the format - // i.e, removing user provided date format from original format - // and then storing it. - base::type::string_t formatCopy = userFormat; - m_flags = 0x0; - auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { - std::size_t foundAt = base::type::string_t::npos; - while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos){ - if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { - if (hasFlag(flag)) { - // If we already have flag we remove the escape chars so that '%%' is turned to '%' - // even after specifier resolution - this is because we only replaceFirst specifier - formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1); - ++foundAt; - } - } else { - if (!hasFlag(flag)) addFlag(flag); - } - } - }; - conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); - conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); - conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); - conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); - conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); - conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); - conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); - conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); - conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); - conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); - conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); - conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); - conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); - conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); - // For date/time we need to extract user's date format first - std::size_t dateIndex = std::string::npos; - if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { - while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { - dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); - } - if (dateIndex != std::string::npos) { - addFlag(base::FormatFlags::DateTime); - updateDateFormat(dateIndex, formatCopy); - } - } - m_format = formatCopy; - updateFormatSpec(); - } - - inline Level level(void) const { - return m_level; - } - - inline const base::type::string_t& userFormat(void) const { - return m_userFormat; - } - - inline const base::type::string_t& format(void) const { - return m_format; - } - - inline const std::string& dateTimeFormat(void) const { - return m_dateTimeFormat; - } - - inline base::type::EnumType flags(void) const { - return m_flags; - } - - inline bool hasFlag(base::FormatFlags flag) const { - return base::utils::hasFlag(flag, m_flags); - } - - virtual void log(el::base::type::ostream_t& os) const { - os << m_format; - } - - protected: - /// @brief Updates date time format if available in currFormat. - /// @param index Index where %datetime, %date or %time was found - /// @param [in,out] currFormat current format that is being used to format - virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL { - if (hasFlag(base::FormatFlags::DateTime)) { - index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier); - } - const base::type::char_t* ptr = currFormat.c_str() + index; - if ((currFormat.size() > index) && (ptr[0] == '{')) { - // User has provided format for date/time - ++ptr; - int count = 1; // Start by 1 in order to remove starting brace - std::stringstream ss; - for (; *ptr; ++ptr, ++count) { - if (*ptr == '}') { - ++count; // In order to remove ending brace - break; - } - ss << *ptr; - } - currFormat.erase(index, count); - m_dateTimeFormat = ss.str(); - } else { - // No format provided, use default - if (hasFlag(base::FormatFlags::DateTime)) { - m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat); - } - } - } - - /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level - virtual void updateFormatSpec(void) ELPP_FINAL { - // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. - if (m_level == Level::Debug) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kDebugLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kDebugLevelShortLogValue); - } else if (m_level == Level::Info) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kInfoLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kInfoLevelShortLogValue); - } else if (m_level == Level::Warning) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kWarningLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kWarningLevelShortLogValue); - } else if (m_level == Level::Error) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kErrorLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kErrorLevelShortLogValue); - } else if (m_level == Level::Fatal) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kFatalLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kFatalLevelShortLogValue); - } else if (m_level == Level::Verbose) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kVerboseLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kVerboseLevelShortLogValue); - } else if (m_level == Level::Trace) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kTraceLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kTraceLevelShortLogValue); - } - if (hasFlag(base::FormatFlags::User)) { - std::string s = base::utils::s_currentUser; - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, - base::utils::s_currentUser); - } - if (hasFlag(base::FormatFlags::Host)) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, - base::utils::s_currentHost); - } - // Ignore Level::Global and Level::Unknown - } - - inline void addFlag(base::FormatFlags flag) { - base::utils::addFlag(flag, &m_flags); - } - - private: - Level m_level; - base::type::string_t m_userFormat; - base::type::string_t m_format; - std::string m_dateTimeFormat; - base::type::EnumType m_flags; - friend class el::Logger; // To resolve loggerId format specifier easily - }; +/// @brief Represents log format containing flags and date format. This is used internally to start initial log +class LogFormat : public Loggable { + public: + LogFormat(void); + LogFormat(Level level, const base::type::string_t& format); + LogFormat(const LogFormat& logFormat); + LogFormat(LogFormat&& logFormat); + LogFormat& operator=(const LogFormat& logFormat); + virtual ~LogFormat(void) {} + bool operator==(const LogFormat& other); + + /// @brief Updates format to be used while logging. + /// @param userFormat User provided format + void parseFromFormat(const base::type::string_t& userFormat); + + inline Level level(void) const { + return m_level; + } + + inline const base::type::string_t& userFormat(void) const { + return m_userFormat; + } + + inline const base::type::string_t& format(void) const { + return m_format; + } + + inline const std::string& dateTimeFormat(void) const { + return m_dateTimeFormat; + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline bool hasFlag(base::FormatFlags flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + virtual void log(el::base::type::ostream_t& os) const { + os << m_format; + } + + protected: + /// @brief Updates date time format if available in currFormat. + /// @param index Index where %datetime, %date or %time was found + /// @param [in,out] currFormat current format that is being used to format + virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL; + + /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level + virtual void updateFormatSpec(void) ELPP_FINAL; + + inline void addFlag(base::FormatFlags flag) { + base::utils::addFlag(flag, &m_flags); + } + + private: + Level m_level; + base::type::string_t m_userFormat; + base::type::string_t m_format; + std::string m_dateTimeFormat; + base::type::EnumType m_flags; + std::string m_currentUser; + std::string m_currentHost; + friend class el::Logger; // To resolve loggerId format specifier easily +}; } // namespace base /// @brief Resolving function for format specifier -typedef std::function<const char*(void)> FormatSpecifierValueResolver; +typedef std::function<std::string(const LogMessage*)> FormatSpecifierValueResolver; /// @brief User-provided custom format specifier /// @see el::Helpers::installCustomFormatSpecifier /// @see FormatSpecifierValueResolver class CustomFormatSpecifier { -public: - CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : + public: + CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : m_formatSpecifier(formatSpecifier), m_resolver(resolver) {} - inline const char* formatSpecifier(void) const { return m_formatSpecifier; } - inline const FormatSpecifierValueResolver& resolver(void) const { return m_resolver; } - inline bool operator==(const char* formatSpecifier) { - return strcmp(m_formatSpecifier, formatSpecifier) == 0; - } - -private: - const char* m_formatSpecifier; - FormatSpecifierValueResolver m_resolver; + inline const char* formatSpecifier(void) const { + return m_formatSpecifier; + } + inline const FormatSpecifierValueResolver& resolver(void) const { + return m_resolver; + } + inline bool operator==(const char* formatSpecifier) { + return strcmp(m_formatSpecifier, formatSpecifier) == 0; + } + + private: + const char* m_formatSpecifier; + FormatSpecifierValueResolver m_resolver; }; /// @brief Represents single configuration that has representing level, configuration type and a string based value. /// @@ -2436,2373 +1725,1241 @@ private: /// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048"); /// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log"); class Configuration : public Loggable { -public: - Configuration(const Configuration& c) : - m_level(c.m_level), - m_configurationType(c.m_configurationType), - m_value(c.m_value) { - } - - Configuration& operator=(const Configuration& c) { - m_level = c.m_level; - m_configurationType = c.m_configurationType; - m_value = c.m_value; - return *this; - } - - virtual ~Configuration(void) { - } - - /// @brief Full constructor used to sets value of configuration - Configuration(Level level, ConfigurationType configurationType, const std::string& value) : - m_level(level), - m_configurationType(configurationType), - m_value(value) { - } - - /// @brief Gets level of current configuration - inline Level level(void) const { - return m_level; - } - - /// @brief Gets configuration type of current configuration - inline ConfigurationType configurationType(void) const { - return m_configurationType; - } - - /// @brief Gets string based configuration value - inline const std::string& value(void) const { - return m_value; - } - - /// @brief Set string based configuration value - /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values - /// use them in quotes. They will be parsed when configuring - inline void setValue(const std::string& value) { - m_value = value; - } - - virtual inline void log(el::base::type::ostream_t& os) const { - os << LevelHelper::convertToString(m_level) - << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType) - << ELPP_LITERAL(" = ") << m_value.c_str(); - } - - /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. - class Predicate { - public: - Predicate(Level level, ConfigurationType configurationType) : - m_level(level), - m_configurationType(configurationType) { - } - - inline bool operator()(const Configuration* conf) const { - return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); - } - - private: - Level m_level; - ConfigurationType m_configurationType; - }; - -private: + public: + Configuration(const Configuration& c); + Configuration& operator=(const Configuration& c); + + virtual ~Configuration(void) { + } + + /// @brief Full constructor used to sets value of configuration + Configuration(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Gets level of current configuration + inline Level level(void) const { + return m_level; + } + + /// @brief Gets configuration type of current configuration + inline ConfigurationType configurationType(void) const { + return m_configurationType; + } + + /// @brief Gets string based configuration value + inline const std::string& value(void) const { + return m_value; + } + + /// @brief Set string based configuration value + /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values + /// use them in quotes. They will be parsed when configuring + inline void setValue(const std::string& value) { + m_value = value; + } + + virtual void log(el::base::type::ostream_t& os) const; + + /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. + class Predicate { + public: + Predicate(Level level, ConfigurationType configurationType); + + bool operator()(const Configuration* conf) const; + + private: Level m_level; ConfigurationType m_configurationType; - std::string m_value; + }; + + private: + Level m_level; + ConfigurationType m_configurationType; + std::string m_value; }; /// @brief Thread-safe Configuration repository /// /// @detail This repository represents configurations for all the levels and configuration type mapped to a value. class Configurations : public base::utils::RegistryWithPred<Configuration, Configuration::Predicate> { -public: - /// @brief Default constructor with empty repository - Configurations(void) : - m_configurationFile(std::string()), - m_isFromFile(false) { - } - - /// @brief Constructor used to set configurations using configuration file. - /// @param configurationFile Full path to configuration file - /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. - /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. - /// @see parseFromFile(const std::string&, Configurations* base) - /// @see setRemainingToDefault() - Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, Configurations* base = nullptr) : - m_configurationFile(configurationFile), - m_isFromFile(false) { - parseFromFile(configurationFile, base); - if (useDefaultsForRemaining) { - setRemainingToDefault(); - } - } - - virtual ~Configurations(void) { - } - + public: + /// @brief Default constructor with empty repository + Configurations(void); + + /// @brief Constructor used to set configurations using configuration file. + /// @param configurationFile Full path to configuration file + /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. + /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. + /// @see parseFromFile(const std::string&, Configurations* base) + /// @see setRemainingToDefault() + Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, + Configurations* base = nullptr); + + virtual ~Configurations(void) { + } + + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr); + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr); + + /// @brief Sets configuration based-off an existing configurations. + /// @param base Pointer to existing configurations. + void setFromBase(Configurations* base); + + /// @brief Determines whether or not specified configuration type exists in the repository. + /// + /// @detail Returns as soon as first level is found. + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(ConfigurationType configurationType); + + /// @brief Determines whether or not specified configuration type exists for specified level + /// @param level Level to check + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(Level level, ConfigurationType configurationType); + + /// @brief Sets value of configuration for specified level. + /// + /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types + /// ConfigurationType::SubsecondPrecision and ConfigurationType::PerformanceTracking will be ignored if not set for + /// Level::Global because these configurations are not dependant on level. + /// @param level Level to set configuration for (el::Level). + /// @param configurationType Type of configuration (el::ConfigurationType) + /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string + /// from users' point of view. This is then parsed later to be used internally. + /// @see Configuration::setValue(const std::string& value) + /// @see el::Level + /// @see el::ConfigurationType + void set(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets single configuration based on other single configuration. + /// @see set(Level level, ConfigurationType configurationType, const std::string& value) + void set(Configuration* conf); + + inline Configuration* get(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); + return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); + } + + /// @brief Sets configuration for all levels. + /// @param configurationType Type of configuration + /// @param value String based value + /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) + inline void setGlobally(ConfigurationType configurationType, const std::string& value) { + setGlobally(configurationType, value, false); + } + + /// @brief Clears repository so that all the configurations are unset + inline void clear(void) { + base::threading::ScopedLock scopedLock(lock()); + unregisterAll(); + } + + /// @brief Gets configuration file used in parsing this configurations. + /// + /// @detail If this repository was set manually or by text this returns empty string. + inline const std::string& configurationFile(void) const { + return m_configurationFile; + } + + /// @brief Sets configurations to "factory based" configurations. + void setToDefault(void); + + /// @brief Lets you set the remaining configurations to default. + /// + /// @detail By remaining, it means that the level/type a configuration does not exist for. + /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets + /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, + /// true. If you dont do this explicitly (either by calling this function or by using second param in Constructor + /// and try to access a value, an error is thrown + void setRemainingToDefault(void); + + /// @brief Parser used internally to parse configurations from file or text. + /// + /// @detail This class makes use of base::utils::Str. + /// You should not need this unless you are working on some tool for Easylogging++ + class Parser : base::StaticClass { + public: /// @brief Parses configuration from file. /// @param configurationFile Full path to configuration file + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class /// @param base Configurations to base new configuration repository off. This value is used when you want to use /// existing Configurations to base all the values and then set rest of configuration via configuration file. - /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you /// do not proceed without successful parse. - inline bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr) { - // We initial assertion with true because if we have assertion diabled, we want to pass this - // check and if assertion is enabled we will have values re-assigned any way. - bool assertionPassed = true; - ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)), - "Configuration file [" << configurationFile << "] does not exist!"); - if (!assertionPassed) { - return false; - } - bool success = Parser::parseFromFile(configurationFile, this, base); - m_isFromFile = success; - return success; - } - + static bool parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base = nullptr); + /// @brief Parse configurations from configuration string. /// /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary - /// new line characters are provided. + /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse (This is recommended) + /// @param configurationsString the configuration in plain text format + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class /// @param base Configurations to base new configuration repository off. This value is used when you want to use /// existing Configurations to base all the values and then set rest of configuration via configuration text. - /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you - /// do not proceed without successful parse. - inline bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr) { - bool success = Parser::parseFromText(configurationsString, this, base); - if (success) { - m_isFromFile = false; - } - return success; - } - - /// @brief Sets configuration based-off an existing configurations. - /// @param base Pointer to existing configurations. - inline void setFromBase(Configurations* base) { - if (base == nullptr || base == this) { - return; - } - base::threading::ScopedLock scopedLock(base->lock()); - for (Configuration*& conf : base->list()) { - set(conf); - } - } - - /// @brief Determines whether or not specified configuration type exists in the repository. - /// - /// @detail Returns as soon as first level is found. - /// @param configurationType Type of configuration to check existence for. - bool hasConfiguration(ConfigurationType configurationType) { - base::type::EnumType lIndex = LevelHelper::kMinValid; - bool result = false; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) { - result = true; - } - return result; - }); - return result; - } - - /// @brief Determines whether or not specified configuration type exists for specified level - /// @param level Level to check - /// @param configurationType Type of configuration to check existence for. - inline bool hasConfiguration(Level level, ConfigurationType configurationType) { - base::threading::ScopedLock scopedLock(lock()); -#if ELPP_COMPILER_INTEL - // We cant specify template types here, Intel C++ throws compilation error - // "error: type name is not allowed" - return RegistryWithPred::get(level, configurationType) != nullptr; -#else - return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType) != nullptr; -#endif // ELPP_COMPILER_INTEL - } - - /// @brief Sets value of configuration for specified level. - /// - /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types - /// ConfigurationType::MillisecondsWidth and ConfigurationType::PerformanceTracking will be ignored if not set for - /// Level::Global because these configurations are not dependant on level. - /// @param level Level to set configuration for (el::Level). - /// @param configurationType Type of configuration (el::ConfigurationType) - /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string - /// from users' point of view. This is then parsed later to be used internally. - /// @see Configuration::setValue(const std::string& value) - /// @see el::Level - /// @see el::ConfigurationType - inline void set(Level level, ConfigurationType configurationType, const std::string& value) { - base::threading::ScopedLock scopedLock(lock()); - unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex - if (level == Level::Global) { - unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either - } - } - - /// @brief Sets single configuration based on other single configuration. - /// @see set(Level level, ConfigurationType configurationType, const std::string& value) - inline void set(Configuration* conf) { - if (conf == nullptr) { - return; - } - set(conf->level(), conf->configurationType(), conf->value()); - } - - inline Configuration* get(Level level, ConfigurationType configurationType) { - base::threading::ScopedLock scopedLock(lock()); - return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); - } - - /// @brief Sets configuration for all levels. - /// @param configurationType Type of configuration - /// @param value String based value - /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) - inline void setGlobally(ConfigurationType configurationType, const std::string& value) { - setGlobally(configurationType, value, false); + /// @return True if successfully parsed, false otherwise. + static bool parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base = nullptr); + + private: + friend class el::Loggers; + static void ignoreComments(std::string* line); + static bool isLevel(const std::string& line); + static bool isComment(const std::string& line); + static inline bool isConfig(const std::string& line); + static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, + Configurations* conf); + }; + + private: + std::string m_configurationFile; + bool m_isFromFile; + friend class el::Loggers; + + /// @brief Unsafely sets configuration if does not already exist + void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Thread unsafe set + void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); + + /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); +}; + +namespace base { +typedef std::shared_ptr<base::type::fstream_t> FileStreamPtr; +typedef std::map<std::string, FileStreamPtr> LogStreamsReferenceMap; +/// @brief Configurations with data types. +/// +/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. +/// This is to perform faster while writing logs using correct configurations. +/// +/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) +class TypedConfigurations : public base::threading::ThreadSafe { + public: + /// @brief Constructor to initialize (construct) the object off el::Configurations + /// @param configurations Configurations pointer/reference to base this typed configurations off. + /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() + TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference); + + TypedConfigurations(const TypedConfigurations& other); + + virtual ~TypedConfigurations(void) { + } + + const Configurations* configurations(void) const { + return m_configurations; + } + + bool enabled(Level level); + bool toFile(Level level); + const std::string& filename(Level level); + bool toStandardOutput(Level level); + const base::LogFormat& logFormat(Level level); + const base::SubsecondPrecision& subsecondPrecision(Level level = Level::Global); + const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global); + bool performanceTracking(Level level = Level::Global); + base::type::fstream_t* fileStream(Level level); + std::size_t maxLogFileSize(Level level); + std::size_t logFlushThreshold(Level level); + + private: + Configurations* m_configurations; + std::map<Level, bool> m_enabledMap; + std::map<Level, bool> m_toFileMap; + std::map<Level, std::string> m_filenameMap; + std::map<Level, bool> m_toStandardOutputMap; + std::map<Level, base::LogFormat> m_logFormatMap; + std::map<Level, base::SubsecondPrecision> m_subsecondPrecisionMap; + std::map<Level, bool> m_performanceTrackingMap; + std::map<Level, base::FileStreamPtr> m_fileStreamMap; + std::map<Level, std::size_t> m_maxLogFileSizeMap; + std::map<Level, std::size_t> m_logFlushThresholdMap; + base::LogStreamsReferenceMap* m_logStreamsReference; + + friend class el::Helpers; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::LogDispatcher; + + template <typename Conf_T> + inline Conf_T getConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template <typename Conf_T> + inline Conf_T& getConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template <typename Conf_T> + Conf_T unsafeGetConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::map<Level, Conf_T>::const_iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + return Conf_T(); + } } - - /// @brief Clears repository so that all the configurations are unset - inline void clear(void) { - base::threading::ScopedLock scopedLock(lock()); - unregisterAll(); + return it->second; + } + + template <typename Conf_T> + Conf_T& unsafeGetConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::map<Level, Conf_T>::iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + } } - - /// @brief Gets configuration file used in parsing this configurations. - /// - /// @detail If this repository was set manually or by text this returns empty string. - inline const std::string& configurationFile(void) const { - return m_configurationFile; + return it->second; + } + + template <typename Conf_T> + void setValue(Level level, const Conf_T& value, std::map<Level, Conf_T>* confMap, bool includeGlobalLevel = true) { + // If map is empty and we are allowed to add into generic level (Level::Global), do it! + if (confMap->empty() && includeGlobalLevel) { + confMap->insert(std::make_pair(Level::Global, value)); + return; } - - /// @brief Sets configurations to "factory based" configurations. - void setToDefault(void) { - setGlobally(ConfigurationType::Enabled, std::string("true"), true); -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) - setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); -#else - ELPP_UNUSED(base::consts::kDefaultLogFile); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) - setGlobally(ConfigurationType::ToFile, std::string("true"), true); - setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); - setGlobally(ConfigurationType::MillisecondsWidth, std::string("3"), true); - setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true); - setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true); - setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true); - - setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true); - set(Level::Debug, ConfigurationType::Format, std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); - // INFO and WARNING are set to default by Level::Global - set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); - set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); - set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); - set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); + // If same value exist in generic level already, dont add it to explicit level + typename std::map<Level, Conf_T>::iterator it = confMap->find(Level::Global); + if (it != confMap->end() && it->second == value) { + return; } - - /// @brief Lets you set the remaining configurations to default. - /// - /// @detail By remaining, it means that the level/type a configuration does not exist for. - /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets - /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, - /// true. If you dont do this explicitley (either by calling this function or by using second param in Constructor - /// and try to access a value, an error is thrown - void setRemainingToDefault(void) { - base::threading::ScopedLock scopedLock(lock()); - unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) - unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) - unsafeSetIfNotExist(Level::Global, ConfigurationType::ToFile, std::string("true")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::MillisecondsWidth, std::string("3")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); - unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, - std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); - // INFO and WARNING are set to default by Level::Global - unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); - unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); - unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); - unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); + // Now make sure we dont double up values if we really need to add it to explicit level + it = confMap->find(level); + if (it == confMap->end()) { + // Value not found for level, add new + confMap->insert(std::make_pair(level, value)); + } else { + // Value found, just update value + confMap->at(level) = value; } - - /// @brief Parser used internally to parse configurations from file or text. - /// - /// @detail This class makes use of base::utils::Str. - /// You should not need this unless you are working on some tool for Easylogging++ - class Parser : base::StaticClass { - public: - /// @brief Parses configuration from file. - /// @param configurationFile Full path to configuration file - /// @param sender Sender configurations pointer. Usually 'this' is used from calling class - /// @param base Configurations to base new configuration repository off. This value is used when you want to use - /// existing Configurations to base all the values and then set rest of configuration via configuration file. - /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you - /// do not proceed without successful parse. - static bool parseFromFile(const std::string& configurationFile, Configurations* sender, Configurations* base = nullptr) { - sender->setFromBase(base); - std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in); - ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing."); - bool parsedSuccessfully = false; - std::string line = std::string(); - Level currLevel = Level::Unknown; - std::string currConfigStr = std::string(); - std::string currLevelStr = std::string(); - while (fileStream_.good()) { - std::getline(fileStream_, line); - parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); - ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); - } - return parsedSuccessfully; - } - - /// @brief Parse configurations from configuration string. - /// - /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary - /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you - /// do not proceed without successful parse (This is recommended) - /// @param configurationsString - /// @param sender Sender configurations pointer. Usually 'this' is used from calling class - /// @param base Configurations to base new configuration repository off. This value is used when you want to use - /// existing Configurations to base all the values and then set rest of configuration via configuration text. - /// @return True if successfully parsed, false otherwise. - static bool parseFromText(const std::string& configurationsString, Configurations* sender, Configurations* base = nullptr) { - sender->setFromBase(base); - bool parsedSuccessfully = false; - std::stringstream ss(configurationsString); - std::string line = std::string(); - Level currLevel = Level::Unknown; - std::string currConfigStr = std::string(); - std::string currLevelStr = std::string(); - while (std::getline(ss, line)) { - parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); - ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); - } - return parsedSuccessfully; - } - - private: - friend class el::Loggers; - static void ignoreComments(std::string* line) { - std::size_t foundAt = 0; - std::size_t quotesStart = line->find("\""); - std::size_t quotesEnd = std::string::npos; - if (quotesStart != std::string::npos) { - quotesEnd = line->find("\"", quotesStart + 1); - while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') { - // Do not erase slash yet - we will erase it in parseLine(..) while loop - quotesEnd = line->find("\"", quotesEnd + 2); - } - } - if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) { - if (foundAt < quotesEnd) { - foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1); - } - *line = line->substr(0, foundAt); - } - } - static inline bool isLevel(const std::string& line) { - return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); - } - - static inline bool isComment(const std::string& line) { - return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); - } - - static inline bool isConfig(const std::string& line) { - std::size_t assignment = line.find('='); - return line != "" && - ((line[0] >= 65 && line[0] <= 90) || (line[0] >= 97 && line[0] <= 122)) && - (assignment != std::string::npos) && - (line.size() > assignment); - } - - static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, Configurations* conf) { - ConfigurationType currConfig = ConfigurationType::Unknown; - std::string currValue = std::string(); - *line = base::utils::Str::trim(*line); - if (isComment(*line)) return true; - ignoreComments(line); - *line = base::utils::Str::trim(*line); - if (line->empty()) { - // Comment ignored - return true; - } - if (isLevel(*line)) { - if (line->size() <= 2) { - return true; - } - *currLevelStr = line->substr(1, line->size() - 2); - *currLevelStr = base::utils::Str::toUpper(*currLevelStr); - *currLevelStr = base::utils::Str::trim(*currLevelStr); - *currLevel = LevelHelper::convertFromString(currLevelStr->c_str()); - return true; - } - if (isConfig(*line)) { - std::size_t assignment = line->find('='); - *currConfigStr = line->substr(0, assignment); - *currConfigStr = base::utils::Str::toUpper(*currConfigStr); - *currConfigStr = base::utils::Str::trim(*currConfigStr); - currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str()); - currValue = line->substr(assignment + 1); - currValue = base::utils::Str::trim(currValue); - std::size_t quotesStart = currValue.find("\"", 0); - std::size_t quotesEnd = std::string::npos; - if (quotesStart != std::string::npos) { - quotesEnd = currValue.find("\"", quotesStart + 1); - while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') { - currValue = currValue.erase(quotesEnd - 1, 1); - quotesEnd = currValue.find("\"", quotesEnd + 2); - } - } - if (quotesStart != std::string::npos && quotesEnd != std::string::npos) { - // Quote provided - check and strip if valid - ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in [" - << currConfigStr << "]"); - ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]"); - if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) { - // Explicit check in case if assertion is disabled - currValue = currValue.substr(quotesStart + 1, quotesEnd - 1); - } - } - } - ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]"); - ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]"); - if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) { - return false; // unrecognizable level or config - } - conf->set(*currLevel, currConfig, currValue); - return true; - } - }; - -private: - std::string m_configurationFile; - bool m_isFromFile; - friend class el::Loggers; - - /// @brief Unsafely sets configuration if does not already exist - void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { - Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); - if (conf == nullptr) { - unsafeSet(level, configurationType, value); - } + } + + void build(Configurations* configurations); + unsigned long getULong(std::string confVal); + std::string resolveFilename(const std::string& filename); + void insertFile(Level level, const std::string& fullFilename); + bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback); + + inline bool validateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeValidateFileRolling(level, preRollOutCallback); + } +}; +/// @brief Class that keeps record of current line hit for occasional logging +class HitCounter { + public: + HitCounter(void) : + m_filename(""), + m_lineNumber(0), + m_hitCounts(0) { + } + + HitCounter(const char* filename, base::type::LineNumber lineNumber) : + m_filename(filename), + m_lineNumber(lineNumber), + m_hitCounts(0) { + } + + HitCounter(const HitCounter& hitCounter) : + m_filename(hitCounter.m_filename), + m_lineNumber(hitCounter.m_lineNumber), + m_hitCounts(hitCounter.m_hitCounts) { + } + + HitCounter& operator=(const HitCounter& hitCounter) { + if (&hitCounter != this) { + m_filename = hitCounter.m_filename; + m_lineNumber = hitCounter.m_lineNumber; + m_hitCounts = hitCounter.m_hitCounts; } - - /// @brief Thread unsafe set - void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { - Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType); - if (conf == nullptr) { - registerNew(new Configuration(level, configurationType, value)); - } else { - conf->setValue(value); - } - if (level == Level::Global) { - unsafeSetGlobally(configurationType, value, false); - } + return *this; + } + + virtual ~HitCounter(void) { + } + + /// @brief Resets location of current hit counter + inline void resetLocation(const char* filename, base::type::LineNumber lineNumber) { + m_filename = filename; + m_lineNumber = lineNumber; + } + + /// @brief Validates hit counts and resets it if necessary + inline void validateHitCounts(std::size_t n) { + if (m_hitCounts >= base::consts::kMaxLogPerCounter) { + m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); } - - /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true - /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) - void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) { - if (includeGlobalLevel) { - set(Level::Global, configurationType, value); - } - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - set(LevelHelper::castFromInt(lIndex), configurationType, value); - return false; // Do not break lambda function yet as we need to set all levels regardless - }); + ++m_hitCounts; + } + + inline const char* filename(void) const { + return m_filename; + } + + inline base::type::LineNumber lineNumber(void) const { + return m_lineNumber; + } + + inline std::size_t hitCounts(void) const { + return m_hitCounts; + } + + inline void increment(void) { + ++m_hitCounts; + } + + class Predicate { + public: + Predicate(const char* filename, base::type::LineNumber lineNumber) + : m_filename(filename), + m_lineNumber(lineNumber) { } - - /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true - /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) - void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) { - if (includeGlobalLevel) { - unsafeSet(Level::Global, configurationType, value); - } - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value); - return false; // Do not break lambda function yet as we need to set all levels regardless - }); + inline bool operator()(const HitCounter* counter) { + return ((counter != nullptr) && + (strcmp(counter->m_filename, m_filename) == 0) && + (counter->m_lineNumber == m_lineNumber)); } + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + }; + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + std::size_t m_hitCounts; }; +/// @brief Repository for hit counters used across the application +class RegisteredHitCounters : public base::utils::RegistryWithPred<base::HitCounter, base::HitCounter::Predicate> { + public: + /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Gets hit counter registered at specified position + inline const base::HitCounter* getCounter(const char* filename, base::type::LineNumber lineNumber) { + base::threading::ScopedLock scopedLock(lock()); + return get(filename, lineNumber); + } +}; +/// @brief Action to be taken for dispatching +enum class DispatchAction : base::type::EnumType { + None = 1, NormalLog = 2, SysLog = 4, FileOnlyLog = 8, +}; +} // namespace base +template <typename T> +class Callback : protected base::threading::ThreadSafe { + public: + Callback(void) : m_enabled(true) {} + inline bool enabled(void) const { + return m_enabled; + } + inline void setEnabled(bool enabled) { + base::threading::ScopedLock scopedLock(lock()); + m_enabled = enabled; + } + protected: + virtual void handle(const T* handlePtr) = 0; + private: + bool m_enabled; +}; +class LogDispatchData { + public: + LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} + inline const LogMessage* logMessage(void) const { + return m_logMessage; + } + inline base::DispatchAction dispatchAction(void) const { + return m_dispatchAction; + } + private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + + inline void setLogMessage(LogMessage* logMessage) { + m_logMessage = logMessage; + } + inline void setDispatchAction(base::DispatchAction dispatchAction) { + m_dispatchAction = dispatchAction; + } +}; +class LogDispatchCallback : public Callback<LogDispatchData> { + private: + friend class base::LogDispatcher; +}; +class PerformanceTrackingCallback : public Callback<PerformanceTrackingData> { + private: + friend class base::PerformanceTracker; +}; +class LoggerRegistrationCallback : public Callback<Logger> { + private: + friend class base::RegisteredLoggers; +}; +class LogBuilder : base::NoCopy { + public: + LogBuilder() : m_termSupportsColor(base::utils::OS::termSupportsColor()) {} + virtual ~LogBuilder(void) { + ELPP_INTERNAL_INFO(3, "Destroying log builder...") + } + virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; + void convertToColoredOutput(base::type::string_t* logLine, Level level); + private: + bool m_termSupportsColor; + friend class el::base::DefaultLogDispatchCallback; +}; +typedef std::shared_ptr<LogBuilder> LogBuilderPtr; +/// @brief Represents a logger holding ID and configurations we need to write logs +/// +/// @detail This class does not write logs itself instead its used by writer to read configuations from. +class Logger : public base::threading::ThreadSafe, public Loggable { + public: + Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference); + Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMap* logStreamsReference); + Logger(const Logger& logger); + Logger& operator=(const Logger& logger); + + virtual ~Logger(void) { + base::utils::safeDelete(m_typedConfigurations); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << m_id.c_str(); + } + + /// @brief Configures the logger using specified configurations. + void configure(const Configurations& configurations); + + /// @brief Reconfigures logger using existing configurations + void reconfigure(void); + + inline const std::string& id(void) const { + return m_id; + } + + inline const std::string& parentApplicationName(void) const { + return m_parentApplicationName; + } + + inline void setParentApplicationName(const std::string& parentApplicationName) { + m_parentApplicationName = parentApplicationName; + } + + inline Configurations* configurations(void) { + return &m_configurations; + } + + inline base::TypedConfigurations* typedConfigurations(void) { + return m_typedConfigurations; + } + + static bool isValidId(const std::string& id); + + /// @brief Flushes logger to sync all log files for all levels + void flush(void); + + void flush(Level level, base::type::fstream_t* fs); + + inline bool isFlushNeeded(Level level) { + return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); + } + + inline LogBuilder* logBuilder(void) const { + return m_logBuilder.get(); + } + + inline void setLogBuilder(const LogBuilderPtr& logBuilder) { + m_logBuilder = logBuilder; + } + + inline bool enabled(Level level) const { + return m_typedConfigurations->enabled(level); + } -namespace base { - typedef std::shared_ptr<base::type::fstream_t> FileStreamPtr; - typedef std::map<std::string, FileStreamPtr> LogStreamsReferenceMap; - /// @brief Configurations with data types. - /// - /// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. - /// This is to perform faster while writing logs using correct configurations. - /// - /// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) - class TypedConfigurations : public base::threading::ThreadSafe { - public: - /// @brief Constructor to initialize (construct) the object off el::Configurations - /// @param configurations Configurations pointer/reference to base this typed configurations off. - /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() - TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference) { - m_configurations = configurations; - m_logStreamsReference = logStreamsReference; - build(m_configurations); - } - - TypedConfigurations(const TypedConfigurations& other) { - this->m_configurations = other.m_configurations; - this->m_logStreamsReference = other.m_logStreamsReference; - build(m_configurations); - } - - virtual ~TypedConfigurations(void) { - } - - const Configurations* configurations(void) const { - return m_configurations; - } - - inline bool enabled(Level level) { - return getConfigByVal<bool>(level, &m_enabledMap, "enabled"); - } - - inline bool toFile(Level level) { - return getConfigByVal<bool>(level, &m_toFileMap, "toFile"); - } - - inline const std::string& filename(Level level) { - return getConfigByRef<std::string>(level, &m_filenameMap, "filename"); - } - - inline bool toStandardOutput(Level level) { - return getConfigByVal<bool>(level, &m_toStandardOutputMap, "toStandardOutput"); - } - - inline const base::LogFormat& logFormat(Level level) { - return getConfigByRef<base::LogFormat>(level, &m_logFormatMap, "logFormat"); - } - - inline const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global) { - return getConfigByRef<base::MillisecondsWidth>(level, &m_millisecondsWidthMap, "millisecondsWidth"); - } - - inline bool performanceTracking(Level level = Level::Global) { - return getConfigByVal<bool>(level, &m_performanceTrackingMap, "performanceTracking"); - } - - inline base::type::fstream_t* fileStream(Level level) { - return getConfigByRef<base::FileStreamPtr>(level, &m_fileStreamMap, "fileStream").get(); - } - - inline std::size_t maxLogFileSize(Level level) { - return getConfigByVal<std::size_t>(level, &m_maxLogFileSizeMap, "maxLogFileSize"); - } - - inline std::size_t logFlushThreshold(Level level) { - return getConfigByVal<std::size_t>(level, &m_logFlushThresholdMap, "logFlushThreshold"); - } - - private: - Configurations* m_configurations; - std::map<Level, bool> m_enabledMap; - std::map<Level, bool> m_toFileMap; - std::map<Level, std::string> m_filenameMap; - std::map<Level, bool> m_toStandardOutputMap; - std::map<Level, base::LogFormat> m_logFormatMap; - std::map<Level, base::MillisecondsWidth> m_millisecondsWidthMap; - std::map<Level, bool> m_performanceTrackingMap; - std::map<Level, base::FileStreamPtr> m_fileStreamMap; - std::map<Level, std::size_t> m_maxLogFileSizeMap; - std::map<Level, std::size_t> m_logFlushThresholdMap; - base::LogStreamsReferenceMap* m_logStreamsReference; - - friend class el::Helpers; - friend class el::base::MessageBuilder; - friend class el::base::Writer; - friend class el::base::DefaultLogDispatchCallback; - friend class el::base::LogDispatcher; - - template <typename Conf_T> - inline Conf_T getConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) { - base::threading::ScopedLock scopedLock(lock()); - return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope - } - - template <typename Conf_T> - inline Conf_T& getConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) { - base::threading::ScopedLock scopedLock(lock()); - return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope - } - - template <typename Conf_T> - inline Conf_T unsafeGetConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) { - ELPP_UNUSED(confName); - typename std::map<Level, Conf_T>::const_iterator it = confMap->find(level); - if (it == confMap->end()) { - try { - return confMap->at(Level::Global); - } catch (...) { - ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" - << LevelHelper::convertToString(level) << "]" - << std::endl << "Please ensure you have properly configured logger.", false); - return Conf_T(); - } - } - return it->second; - } - - template <typename Conf_T> - inline Conf_T& unsafeGetConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) { - ELPP_UNUSED(confName); - typename std::map<Level, Conf_T>::iterator it = confMap->find(level); - if (it == confMap->end()) { - try { - return confMap->at(Level::Global); - } catch (...) { - ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" - << LevelHelper::convertToString(level) << "]" - << std::endl << "Please ensure you have properly configured logger.", false); - } - } - return it->second; - } - - template <typename Conf_T> - void setValue(Level level, const Conf_T& value, std::map<Level, Conf_T>* confMap, bool includeGlobalLevel = true) { - // If map is empty and we are allowed to add into generic level (Level::Global), do it! - if (confMap->empty() && includeGlobalLevel) { - confMap->insert(std::make_pair(Level::Global, value)); - return; - } - // If same value exist in generic level already, dont add it to explicit level - typename std::map<Level, Conf_T>::iterator it = confMap->find(Level::Global); - if (it != confMap->end() && it->second == value) { - return; - } - // Now make sure we dont double up values if we really need to add it to explicit level - it = confMap->find(level); - if (it == confMap->end()) { - // Value not found for level, add new - confMap->insert(std::make_pair(level, value)); - } else { - // Value found, just update value - confMap->at(level) = value; - } - } - - void build(Configurations* configurations) { - base::threading::ScopedLock scopedLock(lock()); - auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming - base::utils::Str::trim(boolStr); - return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); - }; - setValue(Level::Global, base::FileStreamPtr(NULL), &m_fileStreamMap); - std::vector<Configuration*> withFileSizeLimit; - for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { - Configuration* conf = *it; - // We cannot use switch on strong enums because Intel C++ dont support them yet - if (conf->configurationType() == ConfigurationType::Enabled) { - setValue(conf->level(), getBool(conf->value()), &m_enabledMap); - } else if (conf->configurationType() == ConfigurationType::ToFile) { - setValue(conf->level(), getBool(conf->value()), &m_toFileMap); - } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { - setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); - } else if (conf->configurationType() == ConfigurationType::Filename) { - // We do not yet configure filename but we will configure in another - // loop. This is because if file cannot be created, we will force ToFile - // to be false. Because configuring logger is not necessarily performance - // sensative operation, we can live with another loop; (by the way this loop - // is not very heavy either) - } else if (conf->configurationType() == ConfigurationType::Format) { - setValue(conf->level(), base::LogFormat(conf->level(), - base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap); - } else if (conf->configurationType() == ConfigurationType::MillisecondsWidth) { - setValue(Level::Global, - base::MillisecondsWidth(static_cast<int>(getULong(conf->value()))), &m_millisecondsWidthMap); - } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { - setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); - } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { - setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_maxLogFileSizeMap); -#if !defined(ELPP_NO_DEFAULT_LOG_FILE) - withFileSizeLimit.push_back(conf); -#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) - } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { - setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_logFlushThresholdMap); - } - } - // As mentioned early, we will now set filename configuration in separate loop to deal with non-existent files - for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { - Configuration* conf = *it; - if (conf->configurationType() == ConfigurationType::Filename) { - insertFile(conf->level(), conf->value()); - } - } - for (std::vector<Configuration*>::iterator conf = withFileSizeLimit.begin(); - conf != withFileSizeLimit.end(); ++conf) { - // This is not unsafe as mutex is locked in currect scope - unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); - } - } - - unsigned long getULong(std::string confVal) { - bool valid = true; - base::utils::Str::trim(confVal); - valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(), - [](char c) { return !base::utils::Str::isDigit(c); }) == confVal.end(); - if (!valid) { - valid = false; - ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]"); - return 0; - } - return atol(confVal.c_str()); - } - - std::string resolveFilename(const std::string& filename) { - std::string resultingFilename = filename; - std::size_t dateIndex = std::string::npos; - std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename); - if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) { - while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) { - dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1); - } - if (dateIndex != std::string::npos) { - const char* ptr = resultingFilename.c_str() + dateIndex; - // Goto end of specifier - ptr += dateTimeFormatSpecifierStr.size(); - std::string fmt; - if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) { - // User has provided format for date/time - ++ptr; - int count = 1; // Start by 1 in order to remove starting brace - std::stringstream ss; - for (; *ptr; ++ptr, ++count) { - if (*ptr == '}') { - ++count; // In order to remove ending brace - break; - } - ss << *ptr; - } - resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count); - fmt = ss.str(); - } else { - fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename); - } - base::MillisecondsWidth msWidth(3); - std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &msWidth); - base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename - base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); - } - } - return resultingFilename; - } - - void insertFile(Level level, const std::string& fullFilename) { - if (fullFilename.empty()) - return; - std::string resolvedFilename = resolveFilename(fullFilename); - if (resolvedFilename.empty()) { - std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" - << LevelHelper::convertToString(level) << "]"; - } - std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator); - if (filePath.size() < resolvedFilename.size()) { - base::utils::File::createPath(filePath); - } - auto create = [&](Level level) { - base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename); - base::type::fstream_t* fs = nullptr; - if (filestreamIter == m_logStreamsReference->end()) { - // We need a completely new stream, nothing to share with - fs = base::utils::File::newFileStream(resolvedFilename); - m_filenameMap.insert(std::make_pair(level, resolvedFilename)); - m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs))); - m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level)))); - } else { - // Woops! we have an existing one, share it! - m_filenameMap.insert(std::make_pair(level, filestreamIter->first)); - m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second))); - fs = filestreamIter->second.get(); - } - if (fs == nullptr) { - // We display bad file error from newFileStream() - ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" - << LevelHelper::convertToString(level) << "] to FALSE", false); - setValue(level, false, &m_toFileMap); - } - }; - // If we dont have file conf for any level, create it for Level::Global first - // otherwise create for specified level - create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level); - } - - bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) { - base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get(); - if (fs == nullptr) { - return true; - } - std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); - std::size_t currFileSize = base::utils::File::getSizeOfFile(fs); - if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) { - std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename"); - ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level [" - << LevelHelper::convertToString(level) << "]"); - fs->close(); - PreRollOutCallback(fname.c_str(), currFileSize); - fs->open(fname, std::fstream::out | std::fstream::trunc); - return true; - } - return false; - } - - bool validateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) { - base::threading::ScopedLock scopedLock(lock()); - return unsafeValidateFileRolling(level, PreRollOutCallback); - } - }; - /// @brief Class that keeps record of current line hit for occasional logging - class HitCounter { - public: - HitCounter(void) : - m_filename(""), - m_lineNumber(0), - m_hitCounts(0) { - } - - HitCounter(const char* filename, unsigned long int lineNumber) : - m_filename(filename), - m_lineNumber(lineNumber), - m_hitCounts(0) { - } - - HitCounter(const HitCounter& hitCounter) : - m_filename(hitCounter.m_filename), - m_lineNumber(hitCounter.m_lineNumber), - m_hitCounts(hitCounter.m_hitCounts) { - } - - HitCounter& operator=(const HitCounter& hitCounter) { - m_filename = hitCounter.m_filename; - m_lineNumber = hitCounter.m_lineNumber; - m_hitCounts = hitCounter.m_hitCounts; - return *this; - } - - virtual ~HitCounter(void) { - } - - /// @brief Resets location of current hit counter - inline void resetLocation(const char* filename, unsigned long int lineNumber) { - m_filename = filename; - m_lineNumber = lineNumber; - } - - /// @brief Validates hit counts and resets it if necessary - inline void validateHitCounts(std::size_t n) { - if (m_hitCounts >= base::consts::kMaxLogPerCounter) { - m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); - } - ++m_hitCounts; - } - - inline const char* filename(void) const { - return m_filename; - } - - inline unsigned long int lineNumber(void) const { - return m_lineNumber; - } - - inline std::size_t hitCounts(void) const { - return m_hitCounts; - } - - inline void increment(void) { - ++m_hitCounts; - } - - class Predicate { - public: - Predicate(const char* filename, unsigned long int lineNumber) - : m_filename(filename), - m_lineNumber(lineNumber) { - } - inline bool operator()(const HitCounter* counter) { - return ((counter != nullptr) && - (strcmp(counter->m_filename, m_filename) == 0) && - (counter->m_lineNumber == m_lineNumber)); - } - - private: - const char* m_filename; - unsigned long int m_lineNumber; - }; - - private: - const char* m_filename; - unsigned long int m_lineNumber; - std::size_t m_hitCounts; - }; - /// @brief Repository for hit counters used across the application - class RegisteredHitCounters : public base::utils::RegistryWithPred<base::HitCounter, base::HitCounter::Predicate> { - public: - /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one - /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned - bool validateEveryN(const char* filename, unsigned long int lineNumber, std::size_t n) { - base::threading::ScopedLock scopedLock(lock()); - base::HitCounter* counter = get(filename, lineNumber); - if (counter == nullptr) { - registerNew(counter = new base::HitCounter(filename, lineNumber)); - } - counter->validateHitCounts(n); - bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0); - return result; - } - - /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one - /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned - bool validateAfterN(const char* filename, unsigned long int lineNumber, std::size_t n) { - base::threading::ScopedLock scopedLock(lock()); - base::HitCounter* counter = get(filename, lineNumber); - if (counter == nullptr) { - registerNew(counter = new base::HitCounter(filename, lineNumber)); - } - // Do not use validateHitCounts here since we do not want to reset counter here - // Note the >= instead of > because we are incrementing - // after this check - if (counter->hitCounts() >= n) - return true; - counter->increment(); - return false; - } - - /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one - /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned - bool validateNTimes(const char* filename, unsigned long int lineNumber, std::size_t n) { - base::threading::ScopedLock scopedLock(lock()); - base::HitCounter* counter = get(filename, lineNumber); - if (counter == nullptr) { - registerNew(counter = new base::HitCounter(filename, lineNumber)); - } - counter->increment(); - // Do not use validateHitCounts here since we do not want to reset counter here - if (counter->hitCounts() <= n) - return true; - return false; - } - - /// @brief Gets hit counter registered at specified position - inline const base::HitCounter* getCounter(const char* filename, unsigned long int lineNumber) { - base::threading::ScopedLock scopedLock(lock()); - return get(filename, lineNumber); - } - }; - /// @brief Action to be taken for dispatching - enum class DispatchAction : base::type::EnumType { - None = 1, NormalLog = 2, SysLog = 4, FileOnlyLog = 8, - }; - } // namespace base - template <typename T> - class Callback : protected base::threading::ThreadSafe { - public: - Callback(void) : m_enabled(true) {} - inline bool enabled(void) const { return m_enabled; } - inline void setEnabled(bool enabled) { - base::threading::ScopedLock scopedLock(lock()); - m_enabled = enabled; - } - protected: - virtual void handle(const T* handlePtr) = 0; - private: - bool m_enabled; - }; - class LogDispatchData { - public: - LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} - inline const LogMessage* logMessage(void) const { return m_logMessage; } - inline base::DispatchAction dispatchAction(void) const { return m_dispatchAction; } - private: - LogMessage* m_logMessage; - base::DispatchAction m_dispatchAction; - friend class base::LogDispatcher; - - inline void setLogMessage(LogMessage* logMessage) { m_logMessage = logMessage; } - inline void setDispatchAction(base::DispatchAction dispatchAction) { m_dispatchAction = dispatchAction; } - }; - class LogDispatchCallback : public Callback<LogDispatchData> { - private: - friend class base::LogDispatcher; - }; - class PerformanceTrackingCallback : public Callback<PerformanceTrackingData> { - private: - friend class base::PerformanceTracker; - }; - class LogBuilder : base::NoCopy { - public: - virtual ~LogBuilder(void) { ELPP_INTERNAL_INFO(3, "Destroying log builder...")} - virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; - void convertToColoredOutput(base::type::string_t* logLine, Level level) { - if (!base::utils::s_termSupportsColor) return; - const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); - if (level == Level::Error || level == Level::Fatal) - *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; - else if (level == Level::Warning) - *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; - else if (level == Level::Debug) - *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor; - else if (level == Level::Info) - *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor; - else if (level == Level::Trace) - *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor; - } - private: - friend class el::base::DefaultLogDispatchCallback; - }; - typedef std::shared_ptr<LogBuilder> LogBuilderPtr; - /// @brief Represents a logger holding ID and configurations we need to write logs - /// - /// @detail This class does not write logs itself instead its used by writer to read configuations from. - class Logger : public base::threading::ThreadSafe, public Loggable { - public: - Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) : - m_id(id), - m_typedConfigurations(nullptr), - m_parentApplicationName(std::string()), - m_isConfigured(false), - m_logStreamsReference(logStreamsReference) { - initUnflushedCount(); - } - - Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMap* logStreamsReference) : - m_id(id), - m_typedConfigurations(nullptr), - m_parentApplicationName(std::string()), - m_isConfigured(false), - m_logStreamsReference(logStreamsReference) { - initUnflushedCount(); - configure(configurations); - } - - Logger(const Logger& logger) { - base::utils::safeDelete(m_typedConfigurations); - m_id = logger.m_id; - m_typedConfigurations = logger.m_typedConfigurations; - m_parentApplicationName = logger.m_parentApplicationName; - m_isConfigured = logger.m_isConfigured; - m_configurations = logger.m_configurations; - m_unflushedCount = logger.m_unflushedCount; - m_logStreamsReference = logger.m_logStreamsReference; - } - - Logger& operator=(const Logger& logger) { - base::utils::safeDelete(m_typedConfigurations); - m_id = logger.m_id; - m_typedConfigurations = logger.m_typedConfigurations; - m_parentApplicationName = logger.m_parentApplicationName; - m_isConfigured = logger.m_isConfigured; - m_configurations = logger.m_configurations; - m_unflushedCount = logger.m_unflushedCount; - m_logStreamsReference = logger.m_logStreamsReference; - return *this; - } - - virtual ~Logger(void) { - base::utils::safeDelete(m_typedConfigurations); - } - - virtual inline void log(el::base::type::ostream_t& os) const { - os << m_id.c_str(); - } - - /// @brief Configures the logger using specified configurations. - void configure(const Configurations& configurations) { - m_isConfigured = false; // we set it to false in case if we fail - initUnflushedCount(); - if (m_typedConfigurations != nullptr) { - Configurations* c = const_cast<Configurations*>(m_typedConfigurations->configurations()); - if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { - // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE - flush(); - } - } - base::threading::ScopedLock scopedLock(lock()); - if (m_configurations != configurations) { - m_configurations.setFromBase(const_cast<Configurations*>(&configurations)); - } - base::utils::safeDelete(m_typedConfigurations); - m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); - resolveLoggerFormatSpec(); - m_isConfigured = true; - } - - /// @brief Reconfigures logger using existing configurations - inline void reconfigure(void) { - ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); - configure(m_configurations); - } - - inline const std::string& id(void) const { - return m_id; - } - - inline const std::string& parentApplicationName(void) const { - return m_parentApplicationName; - } - - inline void setParentApplicationName(const std::string& parentApplicationName) { - m_parentApplicationName = parentApplicationName; - } - - inline Configurations* configurations(void) { - return &m_configurations; - } - - inline base::TypedConfigurations* typedConfigurations(void) { - return m_typedConfigurations; - } - - static inline bool isValidId(const std::string& id) { - for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { - if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { - return false; - } - } - return true; - } - /// @brief Flushes logger to sync all log files for all levels - inline void flush(void) { - ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); - base::threading::ScopedLock scopedLock(lock()); - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - flush(LevelHelper::castFromInt(lIndex), nullptr); - return false; - }); - } - - inline void flush(Level level, base::type::fstream_t* fs) { - if (fs == nullptr && m_typedConfigurations->toFile(level)) { - fs = m_typedConfigurations->fileStream(level); - } - if (fs != nullptr) { - fs->flush(); - m_unflushedCount.find(level)->second = 0; - } - } - - inline bool isFlushNeeded(Level level) { - return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); - } - - inline LogBuilder* logBuilder(void) const { - return m_logBuilder.get(); - } - - inline void setLogBuilder(const LogBuilderPtr& logBuilder) { - m_logBuilder = logBuilder; - } - - inline bool enabled(Level level) const { - return m_typedConfigurations->enabled(level); - } - #if ELPP_VARIADIC_TEMPLATES_SUPPORTED -# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ +# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ template <typename T, typename... Args>\ inline void FUNCTION_NAME(const char*, const T&, const Args&...);\ template <typename T>\ inline void FUNCTION_NAME(const T&); - - template <typename T, typename... Args> - inline void verbose(int, const char*, const T&, const Args&...); - - template <typename T> - inline void verbose(int, const T&); - - LOGGER_LEVEL_WRITERS_SIGNATURES(info) - LOGGER_LEVEL_WRITERS_SIGNATURES(debug) - LOGGER_LEVEL_WRITERS_SIGNATURES(warn) - LOGGER_LEVEL_WRITERS_SIGNATURES(error) - LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) - LOGGER_LEVEL_WRITERS_SIGNATURES(trace) -# undef LOGGER_LEVEL_WRITERS_SIGNATURES + + template <typename T, typename... Args> + inline void verbose(int, const char*, const T&, const Args&...); + + template <typename T> + inline void verbose(int, const T&); + + LOGGER_LEVEL_WRITERS_SIGNATURES(info) + LOGGER_LEVEL_WRITERS_SIGNATURES(debug) + LOGGER_LEVEL_WRITERS_SIGNATURES(warn) + LOGGER_LEVEL_WRITERS_SIGNATURES(error) + LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) + LOGGER_LEVEL_WRITERS_SIGNATURES(trace) +# undef LOGGER_LEVEL_WRITERS_SIGNATURES #endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED - private: - std::string m_id; - base::TypedConfigurations* m_typedConfigurations; - base::type::stringstream_t m_stream; - std::string m_parentApplicationName; - bool m_isConfigured; - Configurations m_configurations; - std::map<Level, unsigned int> m_unflushedCount; - base::LogStreamsReferenceMap* m_logStreamsReference; - LogBuilderPtr m_logBuilder; - - friend class el::LogMessage; - friend class el::Loggers; - friend class el::Helpers; - friend class el::base::RegisteredLoggers; - friend class el::base::DefaultLogDispatchCallback; - friend class el::base::MessageBuilder; - friend class el::base::Writer; - friend class el::base::PErrorWriter; - friend class el::base::Storage; - friend class el::base::PerformanceTracker; - friend class el::base::LogDispatcher; - - Logger(void); - + private: + std::string m_id; + base::TypedConfigurations* m_typedConfigurations; + base::type::stringstream_t m_stream; + std::string m_parentApplicationName; + bool m_isConfigured; + Configurations m_configurations; + std::map<Level, unsigned int> m_unflushedCount; + base::LogStreamsReferenceMap* m_logStreamsReference; + LogBuilderPtr m_logBuilder; + + friend class el::LogMessage; + friend class el::Loggers; + friend class el::Helpers; + friend class el::base::RegisteredLoggers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PErrorWriter; + friend class el::base::Storage; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + Logger(void); + #if ELPP_VARIADIC_TEMPLATES_SUPPORTED - template <typename T, typename... Args> - void log_(Level, int, const char*, const T&, const Args&...); - - template <typename T> - inline void log_(Level, int, const T&); - - template <typename T, typename... Args> - void log(Level, const char*, const T&, const Args&...); - - template <typename T> - inline void log(Level, const T&); + template <typename T, typename... Args> + void log_(Level, int, const char*, const T&, const Args&...); + + template <typename T> + inline void log_(Level, int, const T&); + + template <typename T, typename... Args> + void log(Level, const char*, const T&, const Args&...); + + template <typename T> + inline void log(Level, const T&); #endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED - - void initUnflushedCount(void) { - m_unflushedCount.clear(); - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); - return false; - }); - } - - inline base::type::stringstream_t& stream(void) { - return m_stream; - } - - void resolveLoggerFormatSpec(void) const { - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - base::LogFormat* logFormat = - const_cast<base::LogFormat*>(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); - base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); - return false; - }); - } - }; - namespace base { - /// @brief Loggers repository - class RegisteredLoggers : public base::utils::Registry<Logger, std::string> { - public: - explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : - m_defaultLogBuilder(defaultLogBuilder) { - m_defaultConfigurations.setToDefault(); - } - - virtual ~RegisteredLoggers(void) { - unsafeFlushAll(); - } - - inline void setDefaultConfigurations(const Configurations& configurations) { - base::threading::ScopedLock scopedLock(lock()); - m_defaultConfigurations.setFromBase(const_cast<Configurations*>(&configurations)); - } - - inline Configurations* defaultConfigurations(void) { - return &m_defaultConfigurations; - } - - Logger* get(const std::string& id, bool forceCreation = true) { - base::threading::ScopedLock scopedLock(lock()); - Logger* logger_ = base::utils::Registry<Logger, std::string>::get(id); - if (logger_ == nullptr && forceCreation) { - bool validId = Logger::isValidId(id); - if (!validId) { - ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); - return nullptr; - } - logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference); - logger_->m_logBuilder = m_defaultLogBuilder; - registerNew(id, logger_); - } - return logger_; - } - - bool remove(const std::string& id) { - if (id == "default") { - return false; - } - Logger* logger = base::utils::Registry<Logger, std::string>::get(id); - if (logger != nullptr) { - unregister(logger); - } - return true; - } - - inline bool has(const std::string& id) { - return get(id, false) != nullptr; - } - - inline void unregister(Logger*& logger) { - base::threading::ScopedLock scopedLock(lock()); - base::utils::Registry<Logger, std::string>::unregister(logger->id()); - } - - inline base::LogStreamsReferenceMap* logStreamsReference(void) { - return &m_logStreamsReference; - } - - inline void flushAll(void) { - base::threading::ScopedLock scopedLock(lock()); - unsafeFlushAll(); - } - - private: - LogBuilderPtr m_defaultLogBuilder; - Configurations m_defaultConfigurations; - base::LogStreamsReferenceMap m_logStreamsReference; - friend class el::base::Storage; - - inline void unsafeFlushAll(void) { - ELPP_INTERNAL_INFO(1, "Flushing all log files"); - for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin(); - it != m_logStreamsReference.end(); ++it) { - if (it->second.get() == nullptr) continue; - it->second->flush(); - } - } - }; - /// @brief Represents registries for verbose logging - class VRegistry : base::NoCopy, public base::threading::ThreadSafe { - public: - explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { - } - - /// @brief Sets verbose level. Accepted range is 0-9 - inline void setLevel(base::type::VerboseLevel level) { - base::threading::ScopedLock scopedLock(lock()); - if (level < 0) - m_level = 0; - else if (level > 9) - m_level = base::consts::kMaxVerboseLevel; - else - m_level = level; - } - - inline base::type::VerboseLevel level(void) const { - return m_level; - } - - inline void clearModules(void) { - base::threading::ScopedLock scopedLock(lock()); - m_modules.clear(); - } - - inline void clearCategories(void) { - base::threading::ScopedLock scopedLock(lock()); - m_categories.clear(); - } - - void setModules(const char* modules) { - base::threading::ScopedLock scopedLock(lock()); - auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) { - if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) { - std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev))); - ss.str(std::string("")); - ss << chr; - } - if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) { - std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx))); - ss.str(std::string("")); - ss << chr; - } - ss << sfx; - }; - auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) { - if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) { - addSuffix(ss, ".h", nullptr); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".c", ".h"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".cpp", ".c"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".cc", ".cpp"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".cxx", ".cc"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".-inl.h", ".cxx"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".hxx", ".-inl.h"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".hpp", ".hxx"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".hh", ".hpp"); - } - m_modules.insert(std::make_pair(ss.str(), level)); - }; - bool isMod = true; - bool isLevel = false; - std::stringstream ss; - int level = -1; - for (; *modules; ++modules) { - switch (*modules) { - case '=': - isLevel = true; - isMod = false; - break; - case ',': - isLevel = false; - isMod = true; - if (!ss.str().empty() && level != -1) { - insert(ss, level); - ss.str(std::string("")); - level = -1; - } - break; - default: - if (isMod) { - ss << *modules; - } else if (isLevel) { - if (isdigit(*modules)) { - level = static_cast<base::type::VerboseLevel>(*modules) - 48; - } - } - break; - } - } - if (!ss.str().empty() && level != -1) { - insert(ss, level); - } - } - - void setCategories(const char* categories, bool clear = true) { - base::threading::ScopedLock scopedLock(lock()); - auto insert = [&](std::stringstream& ss, Level level) { - m_categories.push_back(std::make_pair(ss.str(), level)); - }; - - if (clear) - m_categories.clear(); - if (!categories) - return; - - bool isCat = true; - bool isLevel = false; - std::stringstream ss; - Level level = Level::Unknown; - for (; *categories; ++categories) { - switch (*categories) { - case ':': - isLevel = true; - isCat = false; - break; - case ',': - isLevel = false; - isCat = true; - if (!ss.str().empty() && level != Level::Unknown) { - insert(ss, level); - ss.str(std::string("")); - level = Level::Unknown; - } - break; - default: - if (isCat) { - ss << *categories; - } else if (isLevel) { - level = LevelHelper::convertFromStringPrefix(categories); - if (level != Level::Unknown) - categories += strlen(LevelHelper::convertToString(level)) - 1; - } - break; - } - } - if (!ss.str().empty() && level != Level::Unknown) { - insert(ss, level); - } - } - - bool allowed(base::type::VerboseLevel vlevel, const char* file) { - base::threading::ScopedLock scopedLock(lock()); - if (m_modules.empty() || file == nullptr) { - return vlevel <= m_level; - } else { - std::map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin(); - for (; it != m_modules.end(); ++it) { - if (base::utils::Str::wildCardMatch(file, it->first.c_str())) { - return vlevel <= it->second; - } - } - if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { - return true; - } - return false; - } - } - - // Log levels are sorted in a weird way... - int priority(Level level) { - if (level == Level::Fatal) return 0; - if (level == Level::Error) return 1; - if (level == Level::Warning) return 2; - if (level == Level::Info) return 3; - if (level == Level::Debug) return 4; - if (level == Level::Verbose) return 5; - if (level == Level::Trace) return 6; - return 7; - } - - bool allowed(Level level, const char* category) { - base::threading::ScopedLock scopedLock(lock()); - if (m_categories.empty() || category == nullptr) { - return false; - } else { - std::deque<std::pair<std::string, Level>>::const_reverse_iterator it = m_categories.rbegin(); - for (; it != m_categories.rend(); ++it) { - if (base::utils::Str::wildCardMatch(category, it->first.c_str())) { - return priority(level) <= priority(it->second); - } - } - return false; - } - } - - inline const std::map<std::string, base::type::VerboseLevel>& modules(void) const { - return m_modules; - } - - void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) { - if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") || - commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) { - setLevel(base::consts::kMaxVerboseLevel); - } else if (commandLineArgs->hasParamWithValue("--v")) { - setLevel(atoi(commandLineArgs->getParamValue("--v"))); - } else if (commandLineArgs->hasParamWithValue("--V")) { - setLevel(atoi(commandLineArgs->getParamValue("--V"))); - } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { - setModules(commandLineArgs->getParamValue("-vmodule")); - } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { - setModules(commandLineArgs->getParamValue("-VMODULE")); - } - } - - /// @brief Whether or not vModules enabled - inline bool vModulesEnabled(void) { - return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); - } - - void setThreadName(const std::string &name) { - m_threadNames[base::threading::getCurrentThreadId()] = name; - } - - std::string getThreadName(const std::string& name) { - std::map<std::string, std::string>::const_iterator it = m_threadNames.find(name); - if (it == m_threadNames.end()) - return name; - return it->second; - } - - void setFilenameCommonPrefix(const std::string &prefix) { - m_filenameCommonPrefix = prefix; - } - - std::string getFilenameCommonPrefix() { - return m_filenameCommonPrefix; - } - private: - base::type::VerboseLevel m_level; - base::type::EnumType* m_pFlags; - std::map<std::string, base::type::VerboseLevel> m_modules; - std::deque<std::pair<std::string, Level>> m_categories; - std::map<std::string, std::string> m_threadNames; - std::string m_filenameCommonPrefix; - }; - } // namespace base - class LogMessage { - public: - LogMessage(Level level, const std::string& file, unsigned long int line, const std::string& func, - base::type::VerboseLevel verboseLevel, Logger* logger) : - m_level(level), m_file(file), m_line(line), m_func(func), - m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str()) { - } - inline Level level(void) const { return m_level; } - inline const std::string& file(void) const { return m_file; } - inline unsigned long int line(void) const { return m_line; } // NOLINT - inline const std::string& func(void) const { return m_func; } - inline base::type::VerboseLevel verboseLevel(void) const { return m_verboseLevel; } - inline Logger* logger(void) const { return m_logger; } - inline const base::type::string_t& message(void) const { return m_message; } - private: - Level m_level; - std::string m_file; - unsigned long int m_line; - std::string m_func; - base::type::VerboseLevel m_verboseLevel; - Logger* m_logger; - base::type::string_t m_message; - }; - namespace base { + + void initUnflushedCount(void); + + inline base::type::stringstream_t& stream(void) { + return m_stream; + } + + void resolveLoggerFormatSpec(void) const; +}; +namespace base { +/// @brief Loggers repository +class RegisteredLoggers : public base::utils::Registry<Logger, std::string> { + public: + explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder); + + virtual ~RegisteredLoggers(void) { + unsafeFlushAll(); + } + + inline void setDefaultConfigurations(const Configurations& configurations) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultConfigurations.setFromBase(const_cast<Configurations*>(&configurations)); + } + + inline Configurations* defaultConfigurations(void) { + return &m_defaultConfigurations; + } + + Logger* get(const std::string& id, bool forceCreation = true); + + template <typename T> + inline bool installLoggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::installCallback<T, base::type::LoggerRegistrationCallbackPtr>(id, + &m_loggerRegistrationCallbacks); + } + + template <typename T> + inline void uninstallLoggerRegistrationCallback(const std::string& id) { + base::utils::Utils::uninstallCallback<T, base::type::LoggerRegistrationCallbackPtr>(id, &m_loggerRegistrationCallbacks); + } + + template <typename T> + inline T* loggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::callback<T, base::type::LoggerRegistrationCallbackPtr>(id, &m_loggerRegistrationCallbacks); + } + + bool remove(const std::string& id); + + inline bool has(const std::string& id) { + return get(id, false) != nullptr; + } + + inline void unregister(Logger*& logger) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::Registry<Logger, std::string>::unregister(logger->id()); + } + + inline base::LogStreamsReferenceMap* logStreamsReference(void) { + return &m_logStreamsReference; + } + + inline void flushAll(void) { + base::threading::ScopedLock scopedLock(lock()); + unsafeFlushAll(); + } + + inline void setDefaultLogBuilder(LogBuilderPtr& logBuilderPtr) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultLogBuilder = logBuilderPtr; + } + + private: + LogBuilderPtr m_defaultLogBuilder; + Configurations m_defaultConfigurations; + base::LogStreamsReferenceMap m_logStreamsReference; + std::map<std::string, base::type::LoggerRegistrationCallbackPtr> m_loggerRegistrationCallbacks; + friend class el::base::Storage; + + void unsafeFlushAll(void); +}; +/// @brief Represents registries for verbose logging +class VRegistry : base::NoCopy, public base::threading::ThreadSafe { + public: + explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags); + + /// @brief Sets verbose level. Accepted range is 0-9 + void setLevel(base::type::VerboseLevel level); + + inline base::type::VerboseLevel level(void) const { + return m_level; + } + + inline void clearCategories(void) { + base::threading::ScopedLock scopedLock(lock()); + m_categories.clear(); + } + + inline void clearModules(void) { + base::threading::ScopedLock scopedLock(lock()); + m_modules.clear(); + } + + void setCategories(const char* categories, bool clear = true); + + void setModules(const char* modules); + + bool allowed(Level level, const char* category); + + bool allowed(base::type::VerboseLevel vlevel, const char* file); + + inline const std::map<std::string, base::type::VerboseLevel>& modules(void) const { + return m_modules; + } + + void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs); + + /// @brief Whether or not vModules enabled + inline bool vModulesEnabled(void) { + return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); + } + + inline void setFilenameCommonPrefix(const std::string &prefix) { + m_filenameCommonPrefix = prefix; + } + + inline const std::string &getFilenameCommonPrefix() const { + return m_filenameCommonPrefix; + } + + private: + base::type::VerboseLevel m_level; + base::type::EnumType* m_pFlags; + std::map<std::string, base::type::VerboseLevel> m_modules; + std::deque<std::pair<std::string, Level>> m_categories; + std::string m_filenameCommonPrefix; +}; +} // namespace base +class LogMessage { + public: + LogMessage(Level level, const std::string& file, base::type::LineNumber line, const std::string& func, + base::type::VerboseLevel verboseLevel, Logger* logger) : + m_level(level), m_file(file), m_line(line), m_func(func), + m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str()) { + } + inline Level level(void) const { + return m_level; + } + inline const std::string& file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const std::string& func(void) const { + return m_func; + } + inline base::type::VerboseLevel verboseLevel(void) const { + return m_verboseLevel; + } + inline Logger* logger(void) const { + return m_logger; + } + inline const base::type::string_t& message(void) const { + return m_message; + } + private: + Level m_level; + std::string m_file; + base::type::LineNumber m_line; + std::string m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + base::type::string_t m_message; +}; +namespace base { #if ELPP_ASYNC_LOGGING - class AsyncLogItem { - public: - explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) - : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} - virtual ~AsyncLogItem() {} - inline LogMessage* logMessage(void) { return &m_logMessage; } - inline LogDispatchData* data(void) { return &m_dispatchData; } - inline base::type::string_t logLine(void) { return m_logLine; } - private: - LogMessage m_logMessage; - LogDispatchData m_dispatchData; - base::type::string_t m_logLine; - }; - class AsyncLogQueue : public base::threading::ThreadSafe { - public: - virtual ~AsyncLogQueue() { - ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); - } - - inline AsyncLogItem next(void) { - base::threading::ScopedLock scopedLock(lock()); - AsyncLogItem result = m_queue.front(); - m_queue.pop(); - return result; - } - - inline void push(const AsyncLogItem& item) { - base::threading::ScopedLock scopedLock(lock()); - m_queue.push(item); - } - inline void pop(void) { - base::threading::ScopedLock scopedLock(lock()); - m_queue.pop(); - } - inline AsyncLogItem front(void) { - base::threading::ScopedLock scopedLock(lock()); - return m_queue.front(); - } - inline bool empty(void) { - base::threading::ScopedLock scopedLock(lock()); - return m_queue.empty(); - } - private: - std::queue<AsyncLogItem> m_queue; - }; - class IWorker { - public: - virtual ~IWorker() {} - virtual void start() = 0; - }; +class AsyncLogItem { + public: + explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) + : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} + virtual ~AsyncLogItem() {} + inline LogMessage* logMessage(void) { + return &m_logMessage; + } + inline LogDispatchData* data(void) { + return &m_dispatchData; + } + inline base::type::string_t logLine(void) { + return m_logLine; + } + private: + LogMessage m_logMessage; + LogDispatchData m_dispatchData; + base::type::string_t m_logLine; +}; +class AsyncLogQueue : public base::threading::ThreadSafe { + public: + virtual ~AsyncLogQueue() { + ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); + } + + inline AsyncLogItem next(void) { + base::threading::ScopedLock scopedLock(lock()); + AsyncLogItem result = m_queue.front(); + m_queue.pop(); + return result; + } + + inline void push(const AsyncLogItem& item) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.push(item); + } + inline void pop(void) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.pop(); + } + inline AsyncLogItem front(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.front(); + } + inline bool empty(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.empty(); + } + private: + std::queue<AsyncLogItem> m_queue; +}; +class IWorker { + public: + virtual ~IWorker() {} + virtual void start() = 0; +}; #endif // ELPP_ASYNC_LOGGING - /// @brief Easylogging++ management storage - class Storage : base::NoCopy, public base::threading::ThreadSafe { - public: -#if ELPP_ASYNC_LOGGING - Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : -#else - explicit Storage(const LogBuilderPtr& defaultLogBuilder) : -#endif // ELPP_ASYNC_LOGGING - m_registeredHitCounters(new base::RegisteredHitCounters()), - m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), - m_flags(0x0), - m_vRegistry(new base::VRegistry(0, &m_flags)), +/// @brief Easylogging++ management storage +class Storage : base::NoCopy, public base::threading::ThreadSafe { + public: #if ELPP_ASYNC_LOGGING - m_asyncLogQueue(new base::AsyncLogQueue()), - m_asyncDispatchWorker(asyncDispatchWorker), -#endif // ELPP_ASYNC_LOGGING - m_preRollOutCallback(base::defaultPreRollOutCallback) { - // Register default logger - m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); - // Register performance logger and reconfigure format - Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); - performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); - performanceLogger->reconfigure(); -#if defined(ELPP_SYSLOG) - // Register syslog logger and reconfigure format - Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); - sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); - sysLogLogger->reconfigure(); -#endif // defined(ELPP_SYSLOG) - addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); -#if ELPP_ASYNC_LOGGING - installLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback")); + Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker); #else - installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback")); -#endif // ELPP_ASYNC_LOGGING - installPerformanceTrackingCallback<base::DefaultPerformanceTrackingCallback>(std::string("DefaultPerformanceTrackingCallback")); - ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); -#if ELPP_ASYNC_LOGGING - m_asyncDispatchWorker->start(); -#endif // ELPP_ASYNC_LOGGING - } - - virtual ~Storage(void) { - ELPP_INTERNAL_INFO(4, "Destroying storage"); -#if ELPP_ASYNC_LOGGING - ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); - uninstallLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback")); - installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback")); - ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); - base::utils::safeDelete(m_asyncDispatchWorker); - ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); - base::utils::safeDelete(m_asyncLogQueue); + explicit Storage(const LogBuilderPtr& defaultLogBuilder); #endif // ELPP_ASYNC_LOGGING - ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); - base::utils::safeDelete(m_registeredHitCounters); - ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); - base::utils::safeDelete(m_registeredLoggers); - ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); - base::utils::safeDelete(m_vRegistry); - } - - inline bool validateEveryNCounter(const char* filename, unsigned long int lineNumber, std::size_t occasion) { - return hitCounters()->validateEveryN(filename, lineNumber, occasion); - } - - inline bool validateAfterNCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT - return hitCounters()->validateAfterN(filename, lineNumber, n); - } - - inline bool validateNTimesCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT - return hitCounters()->validateNTimes(filename, lineNumber, n); - } - - inline base::RegisteredHitCounters* hitCounters(void) const { - return m_registeredHitCounters; - } - - inline base::RegisteredLoggers* registeredLoggers(void) const { - return m_registeredLoggers; - } - - inline base::VRegistry* vRegistry(void) const { - return m_vRegistry; - } - + + virtual ~Storage(void); + + inline bool validateEveryNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t occasion) { + return hitCounters()->validateEveryN(filename, lineNumber, occasion); + } + + inline bool validateAfterNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateAfterN(filename, lineNumber, n); + } + + inline bool validateNTimesCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateNTimes(filename, lineNumber, n); + } + + inline base::RegisteredHitCounters* hitCounters(void) const { + return m_registeredHitCounters; + } + + inline base::RegisteredLoggers* registeredLoggers(void) const { + return m_registeredLoggers; + } + + inline base::VRegistry* vRegistry(void) const { + return m_vRegistry; + } + #if ELPP_ASYNC_LOGGING - inline base::AsyncLogQueue* asyncLogQueue(void) const { - return m_asyncLogQueue; - } + inline base::AsyncLogQueue* asyncLogQueue(void) const { + return m_asyncLogQueue; + } #endif // ELPP_ASYNC_LOGGING - - inline const base::utils::CommandLineArgs* commandLineArgs(void) const { - return &m_commandLineArgs; - } - - inline void addFlag(LoggingFlag flag) { - base::utils::addFlag(flag, &m_flags); - } - - inline void removeFlag(LoggingFlag flag) { - base::utils::removeFlag(flag, &m_flags); - } - - inline bool hasFlag(LoggingFlag flag) const { - return base::utils::hasFlag(flag, m_flags); - } - - inline base::type::EnumType flags(void) const { - return m_flags; - } - - inline void setFlags(base::type::EnumType flags) { - m_flags = flags; - } - - inline void setPreRollOutCallback(const PreRollOutCallback& callback) { - m_preRollOutCallback = callback; - } - - inline void unsetPreRollOutCallback(void) { - m_preRollOutCallback = base::defaultPreRollOutCallback; - } - - inline PreRollOutCallback& preRollOutCallback(void) { - return m_preRollOutCallback; - } - - inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { - base::threading::ScopedLock scopedLock(lock()); - return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), - formatSpecifier) != m_customFormatSpecifiers.end(); - } - - inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { - if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { - return; - } - base::threading::ScopedLock scopedLock(lock()); - m_customFormatSpecifiers.push_back(customFormatSpecifier); - } - - inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { - base::threading::ScopedLock scopedLock(lock()); - std::vector<CustomFormatSpecifier>::iterator it = std::find(m_customFormatSpecifiers.begin(), - m_customFormatSpecifiers.end(), formatSpecifier); - if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { - m_customFormatSpecifiers.erase(it); - return true; - } - return false; - } - - const std::vector<CustomFormatSpecifier>* customFormatSpecifiers(void) const { - return &m_customFormatSpecifiers; - } - - inline void setLoggingLevel(Level level) { - m_loggingLevel = level; - } - - template <typename T> - inline bool installLogDispatchCallback(const std::string& id) { - return installCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); - } - - template <typename T> - inline void uninstallLogDispatchCallback(const std::string& id) { - uninstallCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); - } - template <typename T> - inline T* logDispatchCallback(const std::string& id) { - return callback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); - } - - template <typename T> - inline bool installPerformanceTrackingCallback(const std::string& id) { - return installCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks); - } - - template <typename T> - inline void uninstallPerformanceTrackingCallback(const std::string& id) { - uninstallCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks); - } - - template <typename T> - inline T* performanceTrackingCallback(const std::string& id) { - return callback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks); - } - private: - base::RegisteredHitCounters* m_registeredHitCounters; - base::RegisteredLoggers* m_registeredLoggers; - base::type::EnumType m_flags; - base::VRegistry* m_vRegistry; + + inline const base::utils::CommandLineArgs* commandLineArgs(void) const { + return &m_commandLineArgs; + } + + inline void addFlag(LoggingFlag flag) { + base::utils::addFlag(flag, &m_flags); + } + + inline void removeFlag(LoggingFlag flag) { + base::utils::removeFlag(flag, &m_flags); + } + + inline bool hasFlag(LoggingFlag flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline void setFlags(base::type::EnumType flags) { + m_flags = flags; + } + + inline void setPreRollOutCallback(const PreRollOutCallback& callback) { + m_preRollOutCallback = callback; + } + + inline void unsetPreRollOutCallback(void) { + m_preRollOutCallback = base::defaultPreRollOutCallback; + } + + inline PreRollOutCallback& preRollOutCallback(void) { + return m_preRollOutCallback; + } + + bool hasCustomFormatSpecifier(const char* formatSpecifier); + void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier); + bool uninstallCustomFormatSpecifier(const char* formatSpecifier); + + const std::vector<CustomFormatSpecifier>* customFormatSpecifiers(void) const { + return &m_customFormatSpecifiers; + } + + inline void setLoggingLevel(Level level) { + m_loggingLevel = level; + } + + template <typename T> + inline bool installLogDispatchCallback(const std::string& id) { + return base::utils::Utils::installCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); + } + + template <typename T> + inline void uninstallLogDispatchCallback(const std::string& id) { + base::utils::Utils::uninstallCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); + } + template <typename T> + inline T* logDispatchCallback(const std::string& id) { + return base::utils::Utils::callback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks); + } + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + template <typename T> + inline bool installPerformanceTrackingCallback(const std::string& id) { + return base::utils::Utils::installCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, + &m_performanceTrackingCallbacks); + } + + template <typename T> + inline void uninstallPerformanceTrackingCallback(const std::string& id) { + base::utils::Utils::uninstallCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, + &m_performanceTrackingCallbacks); + } + + template <typename T> + inline T* performanceTrackingCallback(const std::string& id) { + return base::utils::Utils::callback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + + /// @brief Sets thread name for current thread. Requires std::thread + inline void setThreadName(const std::string& name) { + if (name.empty()) return; + base::threading::ScopedLock scopedLock(lock()); + m_threadNames[base::threading::getCurrentThreadId()] = name; + } + + inline std::string getThreadName(const std::string& threadId) { + base::threading::ScopedLock scopedLock(lock()); + std::map<std::string, std::string>::const_iterator it = m_threadNames.find(threadId); + if (it == m_threadNames.end()) { + return threadId; + } + return it->second; + } + + private: + base::RegisteredHitCounters* m_registeredHitCounters; + base::RegisteredLoggers* m_registeredLoggers; + base::type::EnumType m_flags; + base::VRegistry* m_vRegistry; #if ELPP_ASYNC_LOGGING - base::AsyncLogQueue* m_asyncLogQueue; - base::IWorker* m_asyncDispatchWorker; + base::AsyncLogQueue* m_asyncLogQueue; + base::IWorker* m_asyncDispatchWorker; #endif // ELPP_ASYNC_LOGGING - base::utils::CommandLineArgs m_commandLineArgs; - PreRollOutCallback m_preRollOutCallback; - std::map<std::string, base::type::LogDispatchCallbackPtr> m_logDispatchCallbacks; - std::map<std::string, base::type::PerformanceTrackingCallbackPtr> m_performanceTrackingCallbacks; - std::vector<CustomFormatSpecifier> m_customFormatSpecifiers; - Level m_loggingLevel; - - friend class el::Helpers; - friend class el::base::DefaultLogDispatchCallback; - friend class el::LogBuilder; - friend class el::base::MessageBuilder; - friend class el::base::Writer; - friend class el::base::PerformanceTracker; - friend class el::base::LogDispatcher; - - void setApplicationArguments(int argc, char** argv) { - m_commandLineArgs.setArgs(argc, argv); - m_vRegistry->setFromArgs(commandLineArgs()); - // default log file -#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) - if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) { - Configurations c; - c.setGlobally(ConfigurationType::Filename, std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam))); - registeredLoggers()->setDefaultConfigurations(c); - for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin(); - it != registeredLoggers()->end(); ++it) { - it->second->configure(c); - } - } -#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) -#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) - if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) { - m_flags = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); - } -#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) - } - - inline void setApplicationArguments(int argc, const char** argv) { - setApplicationArguments(argc, const_cast<char**>(argv)); - } - - template <typename T, typename TPtr> - inline bool installCallback(const std::string& id, std::map<std::string, TPtr>* mapT) { - if (mapT->find(id) == mapT->end()) { - mapT->insert(std::make_pair(id, TPtr(new T()))); - return true; - } - return false; - } - - template <typename T, typename TPtr> - inline void uninstallCallback(const std::string& id, std::map<std::string, TPtr>* mapT) { - if (mapT->find(id) != mapT->end()) { - mapT->erase(id); - } - } - - template <typename T, typename TPtr> - inline T* callback(const std::string& id, std::map<std::string, TPtr>* mapT) { - typename std::map<std::string, TPtr>::iterator iter = mapT->find(id); - if (iter != mapT->end()) { - return static_cast<T*>(iter->second.get()); - } - return nullptr; - } - }; - extern ELPP_EXPORT base::type::StoragePointer elStorage; + base::utils::CommandLineArgs m_commandLineArgs; + PreRollOutCallback m_preRollOutCallback; + std::map<std::string, base::type::LogDispatchCallbackPtr> m_logDispatchCallbacks; + std::map<std::string, base::type::PerformanceTrackingCallbackPtr> m_performanceTrackingCallbacks; + std::map<std::string, std::string> m_threadNames; + std::vector<CustomFormatSpecifier> m_customFormatSpecifiers; + Level m_loggingLevel; + + friend class el::Helpers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::LogBuilder; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + void setApplicationArguments(int argc, char** argv); + + inline void setApplicationArguments(int argc, const char** argv) { + setApplicationArguments(argc, const_cast<char**>(argv)); + } +}; +extern ELPP_EXPORT base::type::StoragePointer elStorage; #define ELPP el::base::elStorage - class DefaultLogDispatchCallback : public LogDispatchCallback { - protected: - void handle(const LogDispatchData* data) { - m_data = data; - dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), - m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog)); - } - private: - const LogDispatchData* m_data; - void dispatch(base::type::string_t&& logLine) { - if (m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog) { - if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { - base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream(m_data->logMessage()->level()); - if (fs != nullptr) { - fs->write(logLine.c_str(), logLine.size()); - if (fs->fail()) { - ELPP_INTERNAL_ERROR("Unable to write log to file [" - << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n" - << "Few possible reasons (could be something else):\n" << " * Permission denied\n" - << " * Disk full\n" << " * Disk is not writable", true); - } else { - if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) { - m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs); - } - } - } else { - ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] " - << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " - << m_data->logMessage()->logger()->id() << "]", false); - } - } - if (m_data->dispatchAction() != base::DispatchAction::FileOnlyLog) { - if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { - if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) - m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); - ELPP_COUT << ELPP_COUT_LINE(logLine); - } - } - } -#if defined(ELPP_SYSLOG) - else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { - // Determine syslog priority - int sysLogPriority = 0; - if (m_data->logMessage()->level() == Level::Fatal) - sysLogPriority = LOG_EMERG; - else if (m_data->logMessage()->level() == Level::Error) - sysLogPriority = LOG_ERR; - else if (m_data->logMessage()->level() == Level::Warning) - sysLogPriority = LOG_WARNING; - else if (m_data->logMessage()->level() == Level::Info) - sysLogPriority = LOG_INFO; - else if (m_data->logMessage()->level() == Level::Debug) - sysLogPriority = LOG_DEBUG; - else - sysLogPriority = LOG_NOTICE; -# if defined(ELPP_UNICODE) - char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); - syslog(sysLogPriority, "%s", line); - free(line); -# else - syslog(sysLogPriority, "%s", logLine.c_str()); -# endif - } -#endif // defined(ELPP_SYSLOG) - } - }; +class DefaultLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); + private: + const LogDispatchData* m_data; + void dispatch(base::type::string_t&& logLine); +}; #if ELPP_ASYNC_LOGGING - class AsyncLogDispatchCallback : public LogDispatchCallback { - protected: - void handle(const LogDispatchData* data) { - base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), data->dispatchAction() == base::DispatchAction::NormalLog || data->dispatchAction() == base::DispatchAction::FileOnlyLog); - if ((data->dispatchAction() == base::DispatchAction::NormalLog || data->dispatchAction() == base::DispatchAction::FileOnlyLog) && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) { - if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) - data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level()); - ELPP_COUT << ELPP_COUT_LINE(logLine); - } - // Save resources and only queue if we want to write to file otherwise just ignore handler - if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) { - ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine)); - } - } - }; - class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { - public: - AsyncDispatchWorker() { - setContinueRunning(false); - } - - virtual ~AsyncDispatchWorker() { - setContinueRunning(false); - ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); - clean(); - ELPP_INTERNAL_INFO(6, "Log queue cleaned"); - } - - inline bool clean(void) { - std::mutex m; - std::unique_lock<std::mutex> lk(m); - cv.wait(lk, []{ return !ELPP->asyncLogQueue()->empty(); }); - emptyQueue(); - lk.unlock(); - cv.notify_one(); - return ELPP->asyncLogQueue()->empty(); - } - - inline void emptyQueue(void) { - while (!ELPP->asyncLogQueue()->empty()) { - AsyncLogItem data = ELPP->asyncLogQueue()->next(); - handle(&data); - base::threading::msleep(100); - } - } - - virtual inline void start(void) { - base::threading::msleep(5000); // 5s (why?) - setContinueRunning(true); - std::thread t1(&AsyncDispatchWorker::run, this); - t1.join(); - } - - void handle(AsyncLogItem* logItem) { - LogDispatchData* data = logItem->data(); - LogMessage* logMessage = logItem->logMessage(); - Logger* logger = logMessage->logger(); - base::TypedConfigurations* conf = logger->typedConfigurations(); - base::type::string_t logLine = logItem->logLine(); - if (data->dispatchAction() == base::DispatchAction::NormalLog || data->dispatchAction() == base::DispatchAction::FileOnlyLog) { - if (conf->toFile(logMessage->level())) { - base::type::fstream_t* fs = conf->fileStream(logMessage->level()); - if (fs != nullptr) { - fs->write(logLine.c_str(), logLine.size()); - if (fs->fail()) { - ELPP_INTERNAL_ERROR("Unable to write log to file [" - << conf->filename(logMessage->level()) << "].\n" - << "Few possible reasons (could be something else):\n" << " * Permission denied\n" - << " * Disk full\n" << " * Disk is not writable", true); - } else { - if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) { - logger->flush(logMessage->level(), fs); - } - } - } else { - ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] " - << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false); - } - } - } -# if defined(ELPP_SYSLOG) - else if (data->dispatchAction() == base::DispatchAction::SysLog) { - // Determine syslog priority - int sysLogPriority = 0; - if (logMessage->level() == Level::Fatal) - sysLogPriority = LOG_EMERG; - else if (logMessage->level() == Level::Error) - sysLogPriority = LOG_ERR; - else if (logMessage->level() == Level::Warning) - sysLogPriority = LOG_WARNING; - else if (logMessage->level() == Level::Info) - sysLogPriority = LOG_INFO; - else if (logMessage->level() == Level::Debug) - sysLogPriority = LOG_DEBUG; - else - sysLogPriority = LOG_NOTICE; -# if defined(ELPP_UNICODE) - char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); - syslog(sysLogPriority, "%s", line); - free(line); -# else - syslog(sysLogPriority, "%s", logLine.c_str()); -# endif - } -# endif // defined(ELPP_SYSLOG) - } - - void run(void) { - while (continueRunning()) { - emptyQueue(); - base::threading::msleep(10); // 10ms - } - } - - void setContinueRunning(bool value) { - base::threading::ScopedLock scopedLock(m_continueRunningMutex); - m_continueRunning = value; - } - - bool continueRunning(void) const { - return m_continueRunning; - } - private: - std::condition_variable cv; - bool m_continueRunning; - base::threading::Mutex m_continueRunningMutex; - }; +class AsyncLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); +}; +class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { + public: + AsyncDispatchWorker(); + virtual ~AsyncDispatchWorker(); + + bool clean(void); + void emptyQueue(void); + virtual void start(void); + void handle(AsyncLogItem* logItem); + void run(void); + + void setContinueRunning(bool value) { + base::threading::ScopedLock scopedLock(m_continueRunningMutex); + m_continueRunning = value; + } + + bool continueRunning(void) const { + return m_continueRunning; + } + private: + std::condition_variable cv; + bool m_continueRunning; + base::threading::Mutex m_continueRunningMutex; +}; #endif // ELPP_ASYNC_LOGGING - } // namespace base - namespace base { - class DefaultLogBuilder : public LogBuilder { - public: - base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const { - base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations(); - const base::LogFormat* logFormat = &tc->logFormat(logMessage->level()); - base::type::string_t logLine = logFormat->format(); - char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; - const char* bufLim = buff + sizeof(buff); - if (logFormat->hasFlag(base::FormatFlags::AppName)) { - // App name - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, - logMessage->logger()->parentApplicationName()); - } - if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { - // Thread ID - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, - ELPP->vRegistry()->getThreadName(base::threading::getCurrentThreadId())); - } - if (logFormat->hasFlag(base::FormatFlags::DateTime)) { - // DateTime - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier, - base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), - &tc->millisecondsWidth(logMessage->level()))); - } - if (logFormat->hasFlag(base::FormatFlags::Function)) { - // Function - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func()); - } - if (logFormat->hasFlag(base::FormatFlags::File)) { - // File - base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); - base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff, ELPP->vRegistry()->getFilenameCommonPrefix()); - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); - } - if (logFormat->hasFlag(base::FormatFlags::FileBase)) { - // FileBase - base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); - base::utils::File::buildBaseFilename(logMessage->file(), buff); - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); - } - if (logFormat->hasFlag(base::FormatFlags::Line)) { - // Line - char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); - buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false); - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); - } - if (logFormat->hasFlag(base::FormatFlags::Location)) { - // Location - char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); - base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff, ELPP->vRegistry()->getFilenameCommonPrefix()); - buf = base::utils::Str::addToBuff(buff, buf, bufLim); - buf = base::utils::Str::addToBuff(":", buf, bufLim); - buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false); - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); - } - if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { - // Verbose level - char* buf = base::utils::Str::clearBuff(buff, 1); - buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff)); - } - if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { - // Log message - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); - } -#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) - for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin(); - it != ELPP->customFormatSpecifiers()->end(); ++it) { - std::string fs(it->formatSpecifier()); - base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); - base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, std::string(it->resolver()())); - } -#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) - if (appendNewLine) logLine += ELPP_LITERAL("\n"); - return logLine; - } - }; - /// @brief Dispatches log messages - class LogDispatcher : base::NoCopy { - public: - LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) : - m_proceed(proceed), - m_logMessage(std::move(logMessage)), - m_dispatchAction(std::move(dispatchAction)) { - } - - void dispatch(void) { - if (m_proceed && m_dispatchAction == base::DispatchAction::None) { - m_proceed = false; - } - if (!m_proceed) { - return; - } - // We minimize the time of ELPP's lock - this lock is released after log is written - base::threading::ScopedLock scopedLock(ELPP->lock()); - base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations; - if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { - tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback()); - } - LogDispatchCallback* callback = nullptr; - LogDispatchData data; - for (const std::pair<std::string, base::type::LogDispatchCallbackPtr>& h - : ELPP->m_logDispatchCallbacks) { - callback = h.second.get(); - if (callback != nullptr && callback->enabled()) { - data.setLogMessage(&m_logMessage); - data.setDispatchAction(m_dispatchAction); - callback->acquireLock(); - callback->handle(&data); - callback->releaseLock(); - } - } - } - - private: - bool m_proceed; - LogMessage m_logMessage; - base::DispatchAction m_dispatchAction; - }; +} // namespace base +namespace base { +class DefaultLogBuilder : public LogBuilder { + public: + base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const; +}; +/// @brief Dispatches log messages +class LogDispatcher : base::NoCopy { + public: + LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) : + m_proceed(proceed), + m_logMessage(std::move(logMessage)), + m_dispatchAction(std::move(dispatchAction)) { + } + + void dispatch(void); + + private: + bool m_proceed; + LogMessage m_logMessage; + base::DispatchAction m_dispatchAction; +}; #if defined(ELPP_STL_LOGGING) - /// @brief Workarounds to write some STL logs - /// - /// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers - /// of same type and provide iterator interface and pass it on to writeIterator(). - /// Remember, this is passed by value in constructor so that we dont change original containers. - /// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) - namespace workarounds { - /// @brief Abstract IterableContainer template that provides interface for iterable classes of type T - template <typename T, typename Container> - class IterableContainer { - public: - typedef typename Container::iterator iterator; - typedef typename Container::const_iterator const_iterator; - IterableContainer(void) {} - virtual ~IterableContainer(void) {} - iterator begin(void) { return getContainer().begin(); } - iterator end(void) { return getContainer().end(); } - private: - virtual Container& getContainer(void) = 0; - }; - /// @brief Implements IterableContainer and provides iterable std::priority_queue class - template<typename T, typename Container = std::vector<T>, typename Comparator = std::less<typename Container::value_type>> - class IterablePriorityQueue : public IterableContainer<T, Container>, public std::priority_queue<T, Container, Comparator> { - public: - IterablePriorityQueue(std::priority_queue<T, Container, Comparator> queue_) { - std::size_t count_ = 0; - while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { - this->push(queue_.top()); - queue_.pop(); - } - } - private: - inline Container& getContainer(void) { - return this->c; - } - }; - /// @brief Implements IterableContainer and provides iterable std::queue class - template<typename T, typename Container = std::deque<T>> - class IterableQueue : public IterableContainer<T, Container>, public std::queue<T, Container> { - public: - IterableQueue(std::queue<T, Container> queue_) { - std::size_t count_ = 0; - while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { - this->push(queue_.front()); - queue_.pop(); - } - } - private: - inline Container& getContainer(void) { - return this->c; - } - }; - /// @brief Implements IterableContainer and provides iterable std::stack class - template<typename T, typename Container = std::deque<T>> - class IterableStack : public IterableContainer<T, Container>, public std::stack<T, Container> { - public: - IterableStack(std::stack<T, Container> stack_) { - std::size_t count_ = 0; - while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { - this->push(stack_.top()); - stack_.pop(); - } - } - private: - inline Container& getContainer(void) { - return this->c; - } - }; - } // namespace workarounds +/// @brief Workarounds to write some STL logs +/// +/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers +/// of same type and provide iterator interface and pass it on to writeIterator(). +/// Remember, this is passed by value in constructor so that we dont change original containers. +/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) +namespace workarounds { +/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T +template <typename T, typename Container> +class IterableContainer { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + IterableContainer(void) {} + virtual ~IterableContainer(void) {} + iterator begin(void) { + return getContainer().begin(); + } + iterator end(void) { + return getContainer().end(); + } + private: + virtual Container& getContainer(void) = 0; +}; +/// @brief Implements IterableContainer and provides iterable std::priority_queue class +template<typename T, typename Container = std::vector<T>, typename Comparator = std::less<typename Container::value_type>> +class IterablePriorityQueue : public IterableContainer<T, Container>, + public std::priority_queue<T, Container, Comparator> { + public: + IterablePriorityQueue(std::priority_queue<T, Container, Comparator> queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.top()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::queue class +template<typename T, typename Container = std::deque<T>> +class IterableQueue : public IterableContainer<T, Container>, public std::queue<T, Container> { + public: + IterableQueue(std::queue<T, Container> queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.front()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::stack class +template<typename T, typename Container = std::deque<T>> +class IterableStack : public IterableContainer<T, Container>, public std::stack<T, Container> { + public: + IterableStack(std::stack<T, Container> stack_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { + this->push(stack_.top()); + stack_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +} // namespace workarounds #endif // defined(ELPP_STL_LOGGING) - // Log message builder - class MessageBuilder { - public: - MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) {} - void initialize(Logger* logger) { - m_logger = logger; - m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? - ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); - } - -# define ELPP_SIMPLE_LOG(LOG_TYPE)\ -inline MessageBuilder& operator<<(LOG_TYPE msg) {\ +// Log message builder +class MessageBuilder { + public: + MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) {} + void initialize(Logger* logger); + +# define ELPP_SIMPLE_LOG(LOG_TYPE)\ +MessageBuilder& operator<<(LOG_TYPE msg) {\ m_logger->stream() << msg;\ if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\ m_logger->stream() << " ";\ }\ return *this;\ } - - inline MessageBuilder& operator<<(const std::string& msg) { - return operator<<(msg.c_str()); - } - ELPP_SIMPLE_LOG(char) - ELPP_SIMPLE_LOG(bool) - ELPP_SIMPLE_LOG(signed short) - ELPP_SIMPLE_LOG(unsigned short) - ELPP_SIMPLE_LOG(signed int) - ELPP_SIMPLE_LOG(unsigned int) - ELPP_SIMPLE_LOG(signed long) - ELPP_SIMPLE_LOG(unsigned long) - ELPP_SIMPLE_LOG(float) - ELPP_SIMPLE_LOG(double) - ELPP_SIMPLE_LOG(char*) - ELPP_SIMPLE_LOG(const char*) - ELPP_SIMPLE_LOG(const void*) - ELPP_SIMPLE_LOG(long double) - inline MessageBuilder& operator<<(const std::wstring& msg) { - return operator<<(msg.c_str()); - } - inline MessageBuilder& operator<<(const wchar_t* msg) { - if (msg == nullptr) { - m_logger->stream() << base::consts::kNullPointer; - return *this; - } -# if defined(ELPP_UNICODE) - m_logger->stream() << msg; -# else - char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg); - m_logger->stream() << buff_; - free(buff_); -# endif - if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { - m_logger->stream() << " "; - } - return *this; - } - // ostream manipulators - inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { - m_logger->stream() << OStreamMani; - return *this; - } + + inline MessageBuilder& operator<<(const std::string& msg) { + return operator<<(msg.c_str()); + } + ELPP_SIMPLE_LOG(char) + ELPP_SIMPLE_LOG(bool) + ELPP_SIMPLE_LOG(signed short) + ELPP_SIMPLE_LOG(unsigned short) + ELPP_SIMPLE_LOG(signed int) + ELPP_SIMPLE_LOG(unsigned int) + ELPP_SIMPLE_LOG(signed long) + ELPP_SIMPLE_LOG(unsigned long) + ELPP_SIMPLE_LOG(float) + ELPP_SIMPLE_LOG(double) + ELPP_SIMPLE_LOG(char*) + ELPP_SIMPLE_LOG(const char*) + ELPP_SIMPLE_LOG(const void*) + ELPP_SIMPLE_LOG(long double) + inline MessageBuilder& operator<<(const std::wstring& msg) { + return operator<<(msg.c_str()); + } + MessageBuilder& operator<<(const wchar_t* msg); + // ostream manipulators + inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { + m_logger->stream() << OStreamMani; + return *this; + } #define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ template <typename T> \ inline MessageBuilder& operator<<(const temp<T>& template_inst) { \ @@ -4828,190 +2985,190 @@ template <typename T1, typename T2, typename T3, typename T4, typename T5> inline MessageBuilder& operator<<(const temp<T1, T2, T3, T4, T5>& template_inst) { \ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ } - + #if defined(ELPP_STL_LOGGING) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) - template <class T, class Container> - inline MessageBuilder& operator<<(const std::queue<T, Container>& queue_) { - base::workarounds::IterableQueue<T, Container> iterableQueue_ = - static_cast<base::workarounds::IterableQueue<T, Container> >(queue_); - return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); - } - template <class T, class Container> - inline MessageBuilder& operator<<(const std::stack<T, Container>& stack_) { - base::workarounds::IterableStack<T, Container> iterableStack_ = - static_cast<base::workarounds::IterableStack<T, Container> >(stack_); - return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); - } - template <class T, class Container, class Comparator> - inline MessageBuilder& operator<<(const std::priority_queue<T, Container, Comparator>& priorityQueue_) { - base::workarounds::IterablePriorityQueue<T, Container, Comparator> iterablePriorityQueue_ = - static_cast<base::workarounds::IterablePriorityQueue<T, Container, Comparator> >(priorityQueue_); - return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); - } - template <class First, class Second> - inline MessageBuilder& operator<<(const std::pair<First, Second>& pair_) { - m_logger->stream() << ELPP_LITERAL("("); - operator << (static_cast<First>(pair_.first)); - m_logger->stream() << ELPP_LITERAL(", "); - operator << (static_cast<Second>(pair_.second)); - m_logger->stream() << ELPP_LITERAL(")"); - return *this; - } - template <std::size_t Size> - inline MessageBuilder& operator<<(const std::bitset<Size>& bitset_) { - m_logger->stream() << ELPP_LITERAL("["); - operator << (bitset_.to_string()); - m_logger->stream() << ELPP_LITERAL("]"); - return *this; - } -# if defined(ELPP_LOG_STD_ARRAY) - template <class T, std::size_t Size> - inline MessageBuilder& operator<<(const std::array<T, Size>& array) { - return writeIterator(array.begin(), array.end(), array.size()); - } -# endif // defined(ELPP_LOG_STD_ARRAY) -# if defined(ELPP_LOG_UNORDERED_MAP) - ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) - ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) -# endif // defined(ELPP_LOG_UNORDERED_MAP) -# if defined(ELPP_LOG_UNORDERED_SET) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) -# endif // defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) + template <class T, class Container> + inline MessageBuilder& operator<<(const std::queue<T, Container>& queue_) { + base::workarounds::IterableQueue<T, Container> iterableQueue_ = + static_cast<base::workarounds::IterableQueue<T, Container> >(queue_); + return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); + } + template <class T, class Container> + inline MessageBuilder& operator<<(const std::stack<T, Container>& stack_) { + base::workarounds::IterableStack<T, Container> iterableStack_ = + static_cast<base::workarounds::IterableStack<T, Container> >(stack_); + return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); + } + template <class T, class Container, class Comparator> + inline MessageBuilder& operator<<(const std::priority_queue<T, Container, Comparator>& priorityQueue_) { + base::workarounds::IterablePriorityQueue<T, Container, Comparator> iterablePriorityQueue_ = + static_cast<base::workarounds::IterablePriorityQueue<T, Container, Comparator> >(priorityQueue_); + return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); + } + template <class First, class Second> + MessageBuilder& operator<<(const std::pair<First, Second>& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast<First>(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast<Second>(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template <std::size_t Size> + MessageBuilder& operator<<(const std::bitset<Size>& bitset_) { + m_logger->stream() << ELPP_LITERAL("["); + operator << (bitset_.to_string()); + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } +# if defined(ELPP_LOG_STD_ARRAY) + template <class T, std::size_t Size> + inline MessageBuilder& operator<<(const std::array<T, Size>& array) { + return writeIterator(array.begin(), array.end(), array.size()); + } +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) +# endif // defined(ELPP_LOG_UNORDERED_SET) #endif // defined(ELPP_STL_LOGGING) #if defined(ELPP_QT_LOGGING) - inline MessageBuilder& operator<<(const QString& msg) { -# if defined(ELPP_UNICODE) - m_logger->stream() << msg.toStdWString(); -# else - m_logger->stream() << msg.toStdString(); -# endif // defined(ELPP_UNICODE) - return *this; - } - inline MessageBuilder& operator<<(const QByteArray& msg) { - return operator << (QString(msg)); - } - inline MessageBuilder& operator<<(const QStringRef& msg) { - return operator<<(msg.toString()); - } - inline MessageBuilder& operator<<(qint64 msg) { -# if defined(ELPP_UNICODE) - m_logger->stream() << QString::number(msg).toStdWString(); -# else - m_logger->stream() << QString::number(msg).toStdString(); -# endif // defined(ELPP_UNICODE) - return *this; - } - inline MessageBuilder& operator<<(quint64 msg) { -# if defined(ELPP_UNICODE) - m_logger->stream() << QString::number(msg).toStdWString(); -# else - m_logger->stream() << QString::number(msg).toStdString(); -# endif // defined(ELPP_UNICODE) - return *this; - } - inline MessageBuilder& operator<<(QChar msg) { - m_logger->stream() << msg.toLatin1(); - return *this; - } - inline MessageBuilder& operator<<(const QLatin1String& msg) { - m_logger->stream() << msg.latin1(); - return *this; - } - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) - template <typename First, typename Second> - inline MessageBuilder& operator<<(const QPair<First, Second>& pair_) { - m_logger->stream() << ELPP_LITERAL("("); - operator << (static_cast<First>(pair_.first)); - m_logger->stream() << ELPP_LITERAL(", "); - operator << (static_cast<Second>(pair_.second)); - m_logger->stream() << ELPP_LITERAL(")"); - return *this; - } - template <typename K, typename V> - inline MessageBuilder& operator<<(const QMap<K, V>& map_) { - m_logger->stream() << ELPP_LITERAL("["); - QList<K> keys = map_.keys(); - typename QList<K>::const_iterator begin = keys.begin(); - typename QList<K>::const_iterator end = keys.end(); - int max_ = static_cast<int>(base::consts::kMaxLogPerContainer); // to prevent warning - for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { - m_logger->stream() << ELPP_LITERAL("("); - operator << (static_cast<K>(*begin)); - m_logger->stream() << ELPP_LITERAL(", "); - operator << (static_cast<V>(map_.value(*begin))); - m_logger->stream() << ELPP_LITERAL(")"); - m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); - } - if (begin != end) { - m_logger->stream() << ELPP_LITERAL("..."); - } - m_logger->stream() << ELPP_LITERAL("]"); - return *this; - } - template <typename K, typename V> - inline MessageBuilder& operator<<(const QMultiMap<K, V>& map_) { - operator << (static_cast<QMap<K, V>>(map_)); - return *this; - } - template <typename K, typename V> - inline MessageBuilder& operator<<(const QHash<K, V>& hash_) { - m_logger->stream() << ELPP_LITERAL("["); - QList<K> keys = hash_.keys(); - typename QList<K>::const_iterator begin = keys.begin(); - typename QList<K>::const_iterator end = keys.end(); - int max_ = static_cast<int>(base::consts::kMaxLogPerContainer); // prevent type warning - for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { - m_logger->stream() << ELPP_LITERAL("("); - operator << (static_cast<K>(*begin)); - m_logger->stream() << ELPP_LITERAL(", "); - operator << (static_cast<V>(hash_.value(*begin))); - m_logger->stream() << ELPP_LITERAL(")"); - m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); - } - if (begin != end) { - m_logger->stream() << ELPP_LITERAL("..."); - } - m_logger->stream() << ELPP_LITERAL("]"); - return *this; - } - template <typename K, typename V> - inline MessageBuilder& operator<<(const QMultiHash<K, V>& multiHash_) { - operator << (static_cast<QHash<K, V>>(multiHash_)); - return *this; - } + inline MessageBuilder& operator<<(const QString& msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << msg.toStdWString(); +# else + m_logger->stream() << msg.toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(const QByteArray& msg) { + return operator << (QString(msg)); + } + inline MessageBuilder& operator<<(const QStringRef& msg) { + return operator<<(msg.toString()); + } + inline MessageBuilder& operator<<(qint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(quint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(QChar msg) { + m_logger->stream() << msg.toLatin1(); + return *this; + } + inline MessageBuilder& operator<<(const QLatin1String& msg) { + m_logger->stream() << msg.latin1(); + return *this; + } + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) + template <typename First, typename Second> + MessageBuilder& operator<<(const QPair<First, Second>& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast<First>(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast<Second>(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template <typename K, typename V> + MessageBuilder& operator<<(const QMap<K, V>& map_) { + m_logger->stream() << ELPP_LITERAL("["); + QList<K> keys = map_.keys(); + typename QList<K>::const_iterator begin = keys.begin(); + typename QList<K>::const_iterator end = keys.end(); + int max_ = static_cast<int>(base::consts::kMaxLogPerContainer); // to prevent warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast<K>(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast<V>(map_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template <typename K, typename V> + inline MessageBuilder& operator<<(const QMultiMap<K, V>& map_) { + operator << (static_cast<QMap<K, V>>(map_)); + return *this; + } + template <typename K, typename V> + MessageBuilder& operator<<(const QHash<K, V>& hash_) { + m_logger->stream() << ELPP_LITERAL("["); + QList<K> keys = hash_.keys(); + typename QList<K>::const_iterator begin = keys.begin(); + typename QList<K>::const_iterator end = keys.end(); + int max_ = static_cast<int>(base::consts::kMaxLogPerContainer); // prevent type warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast<K>(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast<V>(hash_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template <typename K, typename V> + inline MessageBuilder& operator<<(const QMultiHash<K, V>& multiHash_) { + operator << (static_cast<QHash<K, V>>(multiHash_)); + return *this; + } #endif // defined(ELPP_QT_LOGGING) #if defined(ELPP_BOOST_LOGGING) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) #endif // defined(ELPP_BOOST_LOGGING) - - /// @brief Macro used internally that can be used externally to make containers easylogging++ friendly - /// - /// @detail This macro expands to write an ostream& operator<< for container. This container is expected to - /// have begin() and end() methods that return respective iterators - /// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets - /// @param SizeMethod Method used to get size of container. - /// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro - /// for an example usage + + /// @brief Macro used internally that can be used externally to make containers easylogging++ friendly + /// + /// @detail This macro expands to write an ostream& operator<< for container. This container is expected to + /// have begin() and end() methods that return respective iterators + /// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets + /// @param SizeMethod Method used to get size of container. + /// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro + /// for an example usage #define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\ const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \ @@ -5031,323 +3188,212 @@ ss << ELPP_LITERAL("]");\ return ss;\ } #if defined(ELPP_WXWIDGETS_LOGGING) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) -# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) -# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) -# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) +# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) +# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")") #else -# define ELPP_WX_PTR_ENABLED(ContainerType) -# define ELPP_WX_ENABLED(ContainerType) -# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) +# define ELPP_WX_PTR_ENABLED(ContainerType) +# define ELPP_WX_ENABLED(ContainerType) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) #endif // defined(ELPP_WXWIDGETS_LOGGING) - // Other classes - template <class Class> - ELPP_SIMPLE_LOG(const Class&) + // Other classes + template <class Class> + ELPP_SIMPLE_LOG(const Class&) #undef ELPP_SIMPLE_LOG #undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG #undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG #undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG #undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG #undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG - private: - Logger* m_logger; - const base::type::char_t* m_containerLogSeperator; - - template<class Iterator> - inline MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { - m_logger->stream() << ELPP_LITERAL("["); - for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { - operator << (*begin_); - m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); - } - if (begin_ != end_) { - m_logger->stream() << ELPP_LITERAL("..."); - } - m_logger->stream() << ELPP_LITERAL("]"); - if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { - m_logger->stream() << " "; - } - return *this; - } - }; - /// @brief Writes nothing - Used when certain log is disabled - class NullWriter : base::NoCopy { - public: - NullWriter(void) {} - - // Null manipulator - inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { - return *this; - } - - template <typename T> - inline NullWriter& operator<<(const T&) { - return *this; - } - }; - /// @brief Main entry point of each logging - class Writer : base::NoCopy { - public: - Writer(Level level, const char* file, unsigned long int line, - const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, - base::type::VerboseLevel verboseLevel = 0) : - m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), - m_proceed(false), m_dispatchAction(dispatchAction) { - } - - virtual ~Writer(void) { - processDispatch(); - } - - template <typename T> - inline typename std::enable_if<std::is_integral<T>::value, Writer&>::type - operator<<(T log) { + private: + Logger* m_logger; + const base::type::char_t* m_containerLogSeperator; + + template<class Iterator> + MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { + m_logger->stream() << ELPP_LITERAL("["); + for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { + operator << (*begin_); + m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin_ != end_) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } +}; +/// @brief Writes nothing - Used when certain log is disabled +class NullWriter : base::NoCopy { + public: + NullWriter(void) {} + + // Null manipulator + inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { + return *this; + } + + template <typename T> + inline NullWriter& operator<<(const T&) { + return *this; + } + + inline operator bool() { + return true; + } +}; +/// @brief Main entry point of each logging +class Writer : base::NoCopy { + public: + Writer(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + + virtual ~Writer(void) { + processDispatch(); + } + + template <typename T> + inline typename std::enable_if<std::is_integral<T>::value, Writer&>::type + operator<<(T log) { #if ELPP_LOGGING_ENABLED - if (m_proceed) { - m_messageBuilder << log; - } + if (m_proceed) { + m_messageBuilder << log; + } #endif // ELPP_LOGGING_ENABLED - return *this; - } + return *this; + } - template <typename T> - inline typename std::enable_if<!std::is_integral<T>::value, Writer&>::type - operator<<(const T& log) { + template <typename T> + inline typename std::enable_if<!std::is_integral<T>::value, Writer&>::type + operator<<(const T& log) { #if ELPP_LOGGING_ENABLED - if (m_proceed) { - m_messageBuilder << log; - } + if (m_proceed) { + m_messageBuilder << log; + } #endif // ELPP_LOGGING_ENABLED - return *this; - } - - inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { + return *this; + } + + inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { #if ELPP_LOGGING_ENABLED - if (m_proceed) { - m_messageBuilder << log; - } + if (m_proceed) { + m_messageBuilder << log; + } #endif // ELPP_LOGGING_ENABLED - return *this; - } - - Writer& construct(Logger* logger, bool needLock = true) { - m_logger = logger; - initializeLogger(logger->id(), false, needLock); - m_messageBuilder.initialize(m_logger); - return *this; - } - - Writer& construct(int count, const char* loggerIds, ...) { - if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { - va_list loggersList; - va_start(loggersList, loggerIds); - const char* id = loggerIds; - for (int i = 0; i < count; ++i) { - m_loggerIds.push_back(std::string(id)); - id = va_arg(loggersList, const char*); - } - va_end(loggersList); - initializeLogger(m_loggerIds.at(0)); - } else { - initializeLogger(std::string(loggerIds)); - } - m_messageBuilder.initialize(m_logger); - return *this; - } - protected: - Level m_level; - const char* m_file; - const unsigned long int m_line; - const char* m_func; - base::type::VerboseLevel m_verboseLevel; - Logger* m_logger; - bool m_proceed; - base::MessageBuilder m_messageBuilder; - base::DispatchAction m_dispatchAction; - std::vector<std::string> m_loggerIds; - friend class el::Helpers; - - void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true) { - if (lookup) { - m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); - } - if (m_logger == nullptr) { - ELPP->acquireLock(); - if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { - // Somehow default logger has been unregistered. Not good! Register again - ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); - } - ELPP->releaseLock(); // Need to unlock it for next writer - Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) - << "Logger [" << loggerId << "] is not registered yet!"; - m_proceed = false; - } else { - if (needLock) { - m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because - // m_proceed can be changed by lines below - } - if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { - m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : - ELPP->vRegistry()->allowed(m_level, loggerId.c_str()); - } else { - m_proceed = m_logger->enabled(m_level); - } - } - } - - void processDispatch() { -#if ELPP_LOGGING_ENABLED - if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { - bool firstDispatched = false; - base::type::string_t logMessage; - std::size_t i = 0; - do { - if (m_proceed) { - if (firstDispatched) { - m_logger->stream() << logMessage; - } else { - firstDispatched = true; - if (m_loggerIds.size() > 1) { - logMessage = m_logger->stream().str(); - } - } - triggerDispatch(); - } else if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } - if (i + 1 < m_loggerIds.size()) { - initializeLogger(m_loggerIds.at(i + 1)); - } - } while (++i < m_loggerIds.size()); - } else { - if (m_proceed) { - triggerDispatch(); - } else if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } - } -#else - if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } -#endif // ELPP_LOGGING_ENABLED - } - - void triggerDispatch(void) { - if (m_proceed) { - base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel, - m_logger), m_dispatchAction).dispatch(); - } - if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } - if (m_proceed && m_level == Level::Fatal - && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { - base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) - << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; - std::stringstream reasonStream; - reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" - << " If you wish to disable 'abort on fatal log' please use " - << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; - base::utils::abort(1, reasonStream.str()); - } - m_proceed = false; - } - }; - class PErrorWriter : public base::Writer { - public: - PErrorWriter(Level level, const char* file, unsigned long int line, - const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, - base::type::VerboseLevel verboseLevel = 0) : - base::Writer(level, file, line, func, dispatchAction, verboseLevel) { - } - - virtual ~PErrorWriter(void) { - if (m_proceed) { -#if ELPP_COMPILER_MSVC - char buff[256]; - strerror_s(buff, 256, errno); - m_logger->stream() << ": " << buff << " [" << errno << "]"; -#else - m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]"; -#endif - } - } - }; - } // namespace base - // Logging from Logger class. Why this is here? Because we have Storage and Writer class available + return *this; + } + + inline operator bool() { + return true; + } + + Writer& construct(Logger* logger, bool needLock = true); + Writer& construct(int count, const char* loggerIds, ...); + protected: + Level m_level; + const char* m_file; + const base::type::LineNumber m_line; + const char* m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + bool m_proceed; + base::MessageBuilder m_messageBuilder; + base::DispatchAction m_dispatchAction; + std::vector<std::string> m_loggerIds; + friend class el::Helpers; + + void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true); + void processDispatch(); + void triggerDispatch(void); +}; +class PErrorWriter : public base::Writer { + public: + PErrorWriter(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + base::Writer(level, file, line, func, dispatchAction, verboseLevel) { + } + + virtual ~PErrorWriter(void); +}; +} // namespace base +// Logging from Logger class. Why this is here? Because we have Storage and Writer class available #if ELPP_VARIADIC_TEMPLATES_SUPPORTED - template <typename T, typename... Args> - void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { - base::MessageBuilder b; - b.initialize(this); - while (*s) { - if (*s == base::consts::kFormatSpecifierChar) { - if (*(s + 1) == base::consts::kFormatSpecifierChar) { - ++s; - } else { - if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { - ++s; - b << value; - log_(level, vlevel, ++s, args...); - return; - } - } - } - b << *s++; - } - ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); +template <typename T, typename... Args> +void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { + base::MessageBuilder b; + b.initialize(this); + while (*s) { + if (*s == base::consts::kFormatSpecifierChar) { + if (*(s + 1) == base::consts::kFormatSpecifierChar) { + ++s; + } else { + if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { + ++s; + b << value; + log_(level, vlevel, ++s, args...); + return; + } + } } - template <typename T> - inline void Logger::log_(Level level, int vlevel, const T& log) { - if (level == Level::Verbose) { - if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { - base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", - base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; - } else { - stream().str(ELPP_LITERAL("")); - } - } else { - base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; - } - } - template <typename T, typename... Args> - void Logger::log(Level level, const char* s, const T& value, const Args&... args) { - base::threading::ScopedLock scopedLock(lock()); - log_(level, 0, s, value, args...); - } - template <typename T> - inline void Logger::log(Level level, const T& log) { - base::threading::ScopedLock scopedLock(lock()); - log_(level, 0, log); - } -# if ELPP_VERBOSE_LOG - template <typename T, typename... Args> - inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { - base::threading::ScopedLock scopedLock(lock()); - log_(el::Level::Verbose, vlevel, s, value, args...); - } - template <typename T> - inline void Logger::verbose(int vlevel, const T& log) { - base::threading::ScopedLock scopedLock(lock()); - log_(el::Level::Verbose, vlevel, log); - } -# else - template <typename T, typename... Args> - inline void Logger::verbose(int, const char*, const T&, const Args&...) { - return; - } - template <typename T> - inline void Logger::verbose(int, const T&) { - return; + b << *s++; + } + ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); +} +template <typename T> +void Logger::log_(Level level, int vlevel, const T& log) { + if (level == Level::Verbose) { + if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { + base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", + base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; + } else { + stream().str(ELPP_LITERAL("")); } -# endif // ELPP_VERBOSE_LOG -# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ + } else { + base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; + } +} +template <typename T, typename... Args> +inline void Logger::log(Level level, const char* s, const T& value, const Args&... args) { + base::threading::ScopedLock scopedLock(lock()); + log_(level, 0, s, value, args...); +} +template <typename T> +inline void Logger::log(Level level, const T& log) { + base::threading::ScopedLock scopedLock(lock()); + log_(level, 0, log); +} +# if ELPP_VERBOSE_LOG +template <typename T, typename... Args> +inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { + base::threading::ScopedLock scopedLock(lock()); + log_(el::Level::Verbose, vlevel, s, value, args...); +} +template <typename T> +inline void Logger::verbose(int vlevel, const T& log) { + base::threading::ScopedLock scopedLock(lock()); + log_(el::Level::Verbose, vlevel, log); +} +# else +template <typename T, typename... Args> +inline void Logger::verbose(int, const char*, const T&, const Args&...) { + return; +} +template <typename T> +inline void Logger::verbose(int, const T&) { + return; +} +# endif // ELPP_VERBOSE_LOG +# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ template <typename T, typename... Args>\ inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\ log(LOG_LEVEL, s, value, args...);\ @@ -5356,7 +3402,7 @@ template <typename T>\ inline void Logger::FUNCTION_NAME(const T& value) {\ log(LOG_LEVEL, value);\ } -# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ +# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ template <typename T, typename... Args>\ inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\ return;\ @@ -5365,51 +3411,51 @@ template <typename T>\ inline void Logger::FUNCTION_NAME(const T&) {\ return;\ } - -# if ELPP_INFO_LOG - LOGGER_LEVEL_WRITERS(info, Level::Info) -# else - LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) -# endif // ELPP_INFO_LOG -# if ELPP_DEBUG_LOG - LOGGER_LEVEL_WRITERS(debug, Level::Debug) -# else - LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) -# endif // ELPP_DEBUG_LOG -# if ELPP_WARNING_LOG - LOGGER_LEVEL_WRITERS(warn, Level::Warning) -# else - LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) -# endif // ELPP_WARNING_LOG -# if ELPP_ERROR_LOG - LOGGER_LEVEL_WRITERS(error, Level::Error) -# else - LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) -# endif // ELPP_ERROR_LOG -# if ELPP_FATAL_LOG - LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) -# else - LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) -# endif // ELPP_FATAL_LOG -# if ELPP_TRACE_LOG - LOGGER_LEVEL_WRITERS(trace, Level::Trace) -# else - LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) -# endif // ELPP_TRACE_LOG -# undef LOGGER_LEVEL_WRITERS -# undef LOGGER_LEVEL_WRITERS_DISABLED + +# if ELPP_INFO_LOG +LOGGER_LEVEL_WRITERS(info, Level::Info) +# else +LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) +# endif // ELPP_INFO_LOG +# if ELPP_DEBUG_LOG +LOGGER_LEVEL_WRITERS(debug, Level::Debug) +# else +LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) +# endif // ELPP_DEBUG_LOG +# if ELPP_WARNING_LOG +LOGGER_LEVEL_WRITERS(warn, Level::Warning) +# else +LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) +# endif // ELPP_WARNING_LOG +# if ELPP_ERROR_LOG +LOGGER_LEVEL_WRITERS(error, Level::Error) +# else +LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) +# endif // ELPP_ERROR_LOG +# if ELPP_FATAL_LOG +LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) +# else +LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) +# endif // ELPP_FATAL_LOG +# if ELPP_TRACE_LOG +LOGGER_LEVEL_WRITERS(trace, Level::Trace) +# else +LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) +# endif // ELPP_TRACE_LOG +# undef LOGGER_LEVEL_WRITERS +# undef LOGGER_LEVEL_WRITERS_DISABLED #endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED #if ELPP_COMPILER_MSVC -# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs -# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) -# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ +# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs +# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) +# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #else -# if ELPP_COMPILER_CLANG -# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -# else -# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -# endif // ELPP_COMPILER_CLANG +# if ELPP_COMPILER_CLANG +# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# else +# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# endif // ELPP_COMPILER_CLANG #endif // ELPP_COMPILER_MSVC #define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N #define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \ @@ -5417,818 +3463,548 @@ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVAL #define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) #define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \ -if (ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion)) \ +ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion) && \ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) #define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \ -if (ELPP->validateAfterNCounter(__FILE__, __LINE__, n)) \ +ELPP->validateAfterNCounter(__FILE__, __LINE__, n) && \ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) #define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \ -if (ELPP->validateNTimesCounter(__FILE__, __LINE__, n)) \ +ELPP->validateNTimesCounter(__FILE__, __LINE__, n) && \ writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) -#undef ELPP_CURR_FILE_PERFORMANCE_LOGGER -#if defined(ELPP_PERFORMANCE_LOGGER) -# define ELPP_CURR_FILE_PERFORMANCE_LOGGER ELPP_PERFORMANCE_LOGGER -#else -# define ELPP_CURR_FILE_PERFORMANCE_LOGGER el::base::consts::kPerformanceLoggerId -#endif - class PerformanceTrackingData { - public: - enum class DataType : base::type::EnumType { - Checkpoint = 1, Complete = 2 - }; - // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) - explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), - m_dataType(dataType), m_file(""), m_line(0), m_func("") {} - inline const std::string* blockName(void) const; - inline const struct timeval* startTime(void) const; - inline const struct timeval* endTime(void) const; - inline const struct timeval* lastCheckpointTime(void) const; - inline const base::PerformanceTracker* performanceTracker(void) const { return m_performanceTracker; } - inline PerformanceTrackingData::DataType dataType(void) const { return m_dataType; } - inline bool firstCheckpoint(void) const { return m_firstCheckpoint; } - inline std::string checkpointId(void) const { return m_checkpointId; } - inline const char* file(void) const { return m_file; } - inline unsigned long int line(void) const { return m_line; } - inline const char* func(void) const { return m_func; } - inline const base::type::string_t* formattedTimeTaken() const { return &m_formattedTimeTaken; } - inline const std::string& loggerId(void) const; - private: - base::PerformanceTracker* m_performanceTracker; - base::type::string_t m_formattedTimeTaken; - PerformanceTrackingData::DataType m_dataType; - bool m_firstCheckpoint; - std::string m_checkpointId; - const char* m_file; - unsigned long int m_line; - const char* m_func; - inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { - m_performanceTracker = performanceTracker; - m_firstCheckpoint = firstCheckpoint; +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +class PerformanceTrackingData { + public: + enum class DataType : base::type::EnumType { + Checkpoint = 1, Complete = 2 + }; + // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) + explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), + m_dataType(dataType), m_firstCheckpoint(false), m_file(""), m_line(0), m_func("") {} + inline const std::string* blockName(void) const; + inline const struct timeval* startTime(void) const; + inline const struct timeval* endTime(void) const; + inline const struct timeval* lastCheckpointTime(void) const; + inline const base::PerformanceTracker* performanceTracker(void) const { + return m_performanceTracker; + } + inline PerformanceTrackingData::DataType dataType(void) const { + return m_dataType; + } + inline bool firstCheckpoint(void) const { + return m_firstCheckpoint; + } + inline std::string checkpointId(void) const { + return m_checkpointId; + } + inline const char* file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const char* func(void) const { + return m_func; + } + inline const base::type::string_t* formattedTimeTaken() const { + return &m_formattedTimeTaken; + } + inline const std::string& loggerId(void) const; + private: + base::PerformanceTracker* m_performanceTracker; + base::type::string_t m_formattedTimeTaken; + PerformanceTrackingData::DataType m_dataType; + bool m_firstCheckpoint; + std::string m_checkpointId; + const char* m_file; + base::type::LineNumber m_line; + const char* m_func; + inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { + m_performanceTracker = performanceTracker; + m_firstCheckpoint = firstCheckpoint; + } + + friend class el::base::PerformanceTracker; +}; +namespace base { +/// @brief Represents performanceTracker block of code that conditionally adds performance status to log +/// either when goes outside the scope of when checkpoint() is called +class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { + public: + PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, + const std::string& loggerId = std::string(el::base::consts::kPerformanceLoggerId), + bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel); + /// @brief Copy constructor + PerformanceTracker(const PerformanceTracker& t) : + m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), + m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), + m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { + } + virtual ~PerformanceTracker(void); + /// @brief A checkpoint for current performanceTracker block. + void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, + base::type::LineNumber line = __LINE__, + const char* func = ""); + inline Level level(void) const { + return m_level; + } + private: + std::string m_blockName; + base::TimestampUnit m_timestampUnit; + std::string m_loggerId; + bool m_scopedLog; + Level m_level; + bool m_hasChecked; + std::string m_lastCheckpointId; + bool m_enabled; + struct timeval m_startTime, m_endTime, m_lastCheckpointTime; + + PerformanceTracker(void); + + friend class el::PerformanceTrackingData; + friend class base::DefaultPerformanceTrackingCallback; + + const inline base::type::string_t getFormattedTimeTaken() const { + return getFormattedTimeTaken(m_startTime); + } + + const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const; + + virtual inline void log(el::base::type::ostream_t& os) const { + os << getFormattedTimeTaken(); + } +}; +class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { + protected: + void handle(const PerformanceTrackingData* data) { + m_data = data; + base::type::stringstream_t ss; + if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { + ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << + *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); + } else { + ss << ELPP_LITERAL("Performance checkpoint"); + if (!m_data->checkpointId().empty()) { + ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); + } + ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << + *m_data->performanceTracker(); + if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) + && m_data->performanceTracker()->m_hasChecked) { + ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); + if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { + ss << ELPP_LITERAL("last checkpoint"); + } else { + ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); } - - friend class el::base::PerformanceTracker; - }; - namespace base { - /// @brief Represents performanceTracker block of code that conditionally adds performance status to log - /// either when goes outside the scope of when checkpoint() is called - class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { - public: - PerformanceTracker(const std::string& blockName, - base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, - const std::string& loggerId = std::string(ELPP_CURR_FILE_PERFORMANCE_LOGGER), - bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel) : - m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog), - m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) { -#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - // We store it locally so that if user happen to change configuration by the end of scope - // or before calling checkpoint, we still depend on state of configuraton at time of construction - el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false); - m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level); - if (m_enabled) { - base::utils::DateTime::gettimeofday(&m_startTime); - } -#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - } - /// @brief Copy constructor - PerformanceTracker(const PerformanceTracker& t) : - m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), - m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), - m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { - } - virtual ~PerformanceTracker(void) { -#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - if (m_enabled) { - base::threading::ScopedLock scopedLock(lock()); - if (m_scopedLog) { - base::utils::DateTime::gettimeofday(&m_endTime); - base::type::string_t formattedTime = getFormattedTimeTaken(); - PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); - data.init(this); - data.m_formattedTimeTaken = formattedTime; - PerformanceTrackingCallback* callback = nullptr; - for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h - : ELPP->m_performanceTrackingCallbacks) { - callback = h.second.get(); - if (callback != nullptr && callback->enabled()) { - callback->acquireLock(); - callback->handle(&data); - callback->releaseLock(); - } - } - } - } -#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) - } - /// @brief A checkpoint for current performanceTracker block. - void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, unsigned long int line = __LINE__, const char* func = "") { -#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - if (m_enabled) { - base::threading::ScopedLock scopedLock(lock()); - base::utils::DateTime::gettimeofday(&m_endTime); - base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); - PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); - data.init(this); - data.m_checkpointId = id; - data.m_file = file; - data.m_line = line; - data.m_func = func; - data.m_formattedTimeTaken = formattedTime; - PerformanceTrackingCallback* callback = nullptr; - for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h - : ELPP->m_performanceTrackingCallbacks) { - callback = h.second.get(); - if (callback != nullptr && callback->enabled()) { - callback->acquireLock(); - callback->handle(&data); - callback->releaseLock(); - } - } - base::utils::DateTime::gettimeofday(&m_lastCheckpointTime); - m_hasChecked = true; - m_lastCheckpointId = id; - } -#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - ELPP_UNUSED(id); - ELPP_UNUSED(file); - ELPP_UNUSED(line); - ELPP_UNUSED(func); - } - inline Level level(void) const { return m_level; } - private: - std::string m_blockName; - base::TimestampUnit m_timestampUnit; - std::string m_loggerId; - bool m_scopedLog; - Level m_level; - bool m_hasChecked; - std::string m_lastCheckpointId; - bool m_enabled; - struct timeval m_startTime, m_endTime, m_lastCheckpointTime; - - PerformanceTracker(void); - - friend class el::PerformanceTrackingData; - friend class base::DefaultPerformanceTrackingCallback; - - const inline base::type::string_t getFormattedTimeTaken() const { - return getFormattedTimeTaken(m_startTime); - } - - const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const { - if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { - base::type::stringstream_t ss; - ss << base::utils::DateTime::getTimeDifference(m_endTime, - startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast<base::type::EnumType>(m_timestampUnit)].unit; - return ss.str(); - } - return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime, - startTime, m_timestampUnit), m_timestampUnit); - } - - virtual inline void log(el::base::type::ostream_t& os) const { - os << getFormattedTimeTaken(); - } - }; - class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { - protected: - void handle(const PerformanceTrackingData* data) { - m_data = data; - base::type::stringstream_t ss; - if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { - ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); - } else { - ss << ELPP_LITERAL("Performance checkpoint"); - if (!m_data->checkpointId().empty()) { - ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); - } - ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << *m_data->performanceTracker(); - if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) && m_data->performanceTracker()->m_hasChecked) { - ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); - if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { - ss << ELPP_LITERAL("last checkpoint"); - } else { - ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); - } - ss << ELPP_LITERAL(")]"); - } else { - ss << ELPP_LITERAL("]"); - } - } - el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, m_data->loggerId().c_str()) << ss.str(); - } - private: - const PerformanceTrackingData* m_data; - }; - } // namespace base - inline const std::string* PerformanceTrackingData::blockName() const { - return const_cast<const std::string*>(&m_performanceTracker->m_blockName); - } - inline const struct timeval* PerformanceTrackingData::startTime() const { - return const_cast<const struct timeval*>(&m_performanceTracker->m_startTime); - } - inline const struct timeval* PerformanceTrackingData::endTime() const { - return const_cast<const struct timeval*>(&m_performanceTracker->m_endTime); + ss << ELPP_LITERAL(")]"); + } else { + ss << ELPP_LITERAL("]"); + } } - inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { - return const_cast<const struct timeval*>(&m_performanceTracker->m_lastCheckpointTime); + el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, + m_data->loggerId().c_str()) << ss.str(); + } + private: + const PerformanceTrackingData* m_data; +}; +} // namespace base +inline const std::string* PerformanceTrackingData::blockName() const { + return const_cast<const std::string*>(&m_performanceTracker->m_blockName); +} +inline const struct timeval* PerformanceTrackingData::startTime() const { + return const_cast<const struct timeval*>(&m_performanceTracker->m_startTime); +} +inline const struct timeval* PerformanceTrackingData::endTime() const { + return const_cast<const struct timeval*>(&m_performanceTracker->m_endTime); +} +inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { + return const_cast<const struct timeval*>(&m_performanceTracker->m_lastCheckpointTime); +} +inline const std::string& PerformanceTrackingData::loggerId(void) const { + return m_performanceTracker->m_loggerId; +} +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +namespace base { +/// @brief Contains some internal debugging tools like crash handler and stack tracer +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +class StackTrace : base::NoCopy { + public: + static const unsigned int kMaxStack = 64; + static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() + class StackTraceEntry { + public: + StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr); + StackTraceEntry(std::size_t index, char* loc) : + m_index(index), + m_location(loc) { } - inline const std::string& PerformanceTrackingData::loggerId(void) const { return m_performanceTracker->m_loggerId; } - namespace base { - /// @brief Contains some internal debugging tools like crash handler and stack tracer - namespace debug { - class StackTrace : base::NoCopy { - public: - static const std::size_t kMaxStack = 64; - static const std::size_t kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() - class StackTraceEntry { - public: - StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr) { - m_index = index; - m_location = std::string(loc); - m_demangled = std::string(demang); - m_hex = std::string(hex); - m_addr = std::string(addr); - } - StackTraceEntry(std::size_t index, char* loc) { - m_index = index; - m_location = std::string(loc); - } - std::size_t m_index; - std::string m_location; - std::string m_demangled; - std::string m_hex; - std::string m_addr; - friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si) { - ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled - << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr; - return ss; - } - - private: - StackTraceEntry(void); - }; - - StackTrace(void) { - generateNew(); - } - - virtual ~StackTrace(void) { - } - - inline std::vector<StackTraceEntry>& getLatestStack(void) { - return m_stack; - } - - friend inline std::ostream& operator<<(std::ostream& os, const StackTrace& st) { - std::vector<StackTraceEntry>::const_iterator it = st.m_stack.begin(); - while (it != st.m_stack.end()) { - os << " " << *it++ << "\n"; - } - return os; - } - - private: - std::vector<StackTraceEntry> m_stack; - - void generateNew(void) { -#if ELPP_STACKTRACE - m_stack.clear(); - void* stack[kMaxStack]; - std::size_t size = backtrace(stack, kMaxStack); - char** strings = backtrace_symbols(stack, size); - if (size > kStackStart) { // Skip StackTrace c'tor and generateNew - for (std::size_t i = kStackStart; i < size; ++i) { - char* mangName = nullptr; - char* hex = nullptr; - char* addr = nullptr; - for (char* c = strings[i]; *c; ++c) { - switch (*c) { - case '(': - mangName = c; - break; - case '+': - hex = c; - break; - case ')': - addr = c; - break; - default: - break; - } - } - // Perform demangling if parsed properly - if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) { - *mangName++ = '\0'; - *hex++ = '\0'; - *addr++ = '\0'; - int status = 0; - char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status); - // if demangling is successful, output the demangled function name - if (status == 0) { - // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) - StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr); - m_stack.push_back(entry); - } else { - // Not successful - we will use mangled name - StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr); - m_stack.push_back(entry); - } - free(demangName); - } else { - StackTraceEntry entry(i - 1, strings[i]); - m_stack.push_back(entry); - } - } - } - free(strings); -#else - ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); -#endif // ELPP_STACKTRACE - } - }; - static std::string crashReason(int sig) { - std::stringstream ss; - bool foundReason = false; - for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { - if (base::consts::kCrashSignals[i].numb == sig) { - ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; - if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { - ss << std::endl << - " " << base::consts::kCrashSignals[i].brief << std::endl << - " " << base::consts::kCrashSignals[i].detail; - } - foundReason = true; - } - } - if (!foundReason) { - ss << "Application has crashed due to unknown signal [" << sig << "]"; - } - return ss.str(); - } - /// @brief Logs reason of crash from sig - static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { - std::stringstream ss; - ss << "CRASH HANDLED; "; - ss << crashReason(sig); -#if ELPP_STACKTRACE - if (stackTraceIfAvailable) { - ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); - } -#else - ELPP_UNUSED(stackTraceIfAvailable); -#endif // ELPP_STACKTRACE - ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); - } - static inline void crashAbort(int sig) { - base::utils::abort(sig); - } - /// @brief Default application crash handler - /// - /// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program. - static inline void defaultCrashHandler(int sig) { - base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId); - base::debug::crashAbort(sig); - } - /// @brief Handles unexpected crashes - class CrashHandler : base::NoCopy { - public: - typedef void (*Handler)(int); - - explicit CrashHandler(bool useDefault) { - if (useDefault) { - setHandler(defaultCrashHandler); - } - } - explicit CrashHandler(const Handler& cHandler) { - setHandler(cHandler); - } - void setHandler(const Handler& cHandler) { - m_handler = cHandler; -#if defined(ELPP_HANDLE_SIGABRT) - int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] + std::size_t m_index; + std::string m_location; + std::string m_demangled; + std::string m_hex; + std::string m_addr; + friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si); + + private: + StackTraceEntry(void); + }; + + StackTrace(void) { + generateNew(); + } + + virtual ~StackTrace(void) { + } + + inline std::vector<StackTraceEntry>& getLatestStack(void) { + return m_stack; + } + + friend std::ostream& operator<<(std::ostream& os, const StackTrace& st); + + private: + std::vector<StackTraceEntry> m_stack; + + void generateNew(void); +}; +/// @brief Handles unexpected crashes +class CrashHandler : base::NoCopy { + public: + typedef void (*Handler)(int); + + explicit CrashHandler(bool useDefault); + explicit CrashHandler(const Handler& cHandler) { + setHandler(cHandler); + } + void setHandler(const Handler& cHandler); + + private: + Handler m_handler; +}; #else - int i = 1; -#endif // defined(ELPP_HANDLE_SIGABRT) - for (; i < base::consts::kCrashSignalsCount; ++i) { - m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); - } - } - - private: - Handler m_handler; - }; - } // namespace debug - } // namespace base - extern base::debug::CrashHandler elCrashHandler; +class CrashHandler { + public: + explicit CrashHandler(bool) {} +}; +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base +extern base::debug::CrashHandler elCrashHandler; #define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \ el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance) - /// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor - class SysLogInitializer { - public: - SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { +/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor +class SysLogInitializer { + public: + SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { #if defined(ELPP_SYSLOG) - openlog(processIdent, options, facility); + openlog(processIdent, options, facility); #else - ELPP_UNUSED(processIdent); - ELPP_UNUSED(options); - ELPP_UNUSED(facility); + ELPP_UNUSED(processIdent); + ELPP_UNUSED(options); + ELPP_UNUSED(facility); #endif // defined(ELPP_SYSLOG) - } - virtual ~SysLogInitializer(void) { + } + virtual ~SysLogInitializer(void) { #if defined(ELPP_SYSLOG) - closelog(); + closelog(); #endif // defined(ELPP_SYSLOG) - } - }; + } +}; #define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac) - /// @brief Static helpers for developers - class Helpers : base::StaticClass { - public: - /// @brief Shares logging repository (base::Storage) - static inline void setStorage(base::type::StoragePointer storage) { - ELPP = storage; - } - /// @return Main storage repository - static inline base::type::StoragePointer storage() { - return ELPP; - } - /// @brief Sets application arguments and figures out whats active for logging and whats not. - static inline void setArgs(int argc, char** argv) { - ELPP->setApplicationArguments(argc, argv); - } - /// @copydoc setArgs(int argc, char** argv) - static inline void setArgs(int argc, const char** argv) { - ELPP->setApplicationArguments(argc, const_cast<char**>(argv)); - } - /// @brief Overrides default crash handler and installs custom handler. - /// @param crashHandler A functor with no return type that takes single int argument. - /// Handler is a typedef with specification: void (*Handler)(int) - static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { - el::elCrashHandler.setHandler(crashHandler); - } - /// @brief Abort due to crash with signal in parameter - /// @param sig Crash signal - static inline void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0) { - std::stringstream ss; - ss << base::debug::crashReason(sig).c_str(); - ss << " - [Called el::Helpers::crashAbort(" << sig << ")]"; - if (sourceFile != nullptr && strlen(sourceFile) > 0) { - ss << " - Source: " << sourceFile; - if (line > 0) - ss << ":" << line; - else - ss << " (line number not specified)"; - } - base::utils::abort(sig, ss.str()); - } - /// @brief Logs reason of crash as per sig - /// @param sig Crash signal - /// @param stackTraceIfAvailable Includes stack trace if available - /// @param level Logging level - /// @param logger Logger to use for logging - static inline void logCrashReason(int sig, bool stackTraceIfAvailable = false, - Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId) { - el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); - } - /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out - /// (can be useful for backing up) - static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { - ELPP->setPreRollOutCallback(callback); - } - /// @brief Uninstalls pre rollout callback - static inline void uninstallPreRollOutCallback(void) { - ELPP->unsetPreRollOutCallback(); - } - /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched - template <typename T> - static inline bool installLogDispatchCallback(const std::string& id) { - return ELPP->installLogDispatchCallback<T>(id); - } - /// @brief Uninstalls log dispatch callback - template <typename T> - static inline void uninstallLogDispatchCallback(const std::string& id) { - ELPP->uninstallLogDispatchCallback<T>(id); - } - template <typename T> - static inline T* logDispatchCallback(const std::string& id) { - return ELPP->logDispatchCallback<T>(id); - } - /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished - template <typename T> - static inline bool installPerformanceTrackingCallback(const std::string& id) { - return ELPP->installPerformanceTrackingCallback<T>(id); - } - /// @brief Uninstalls post performance tracking handler - template <typename T> - static inline void uninstallPerformanceTrackingCallback(const std::string& id) { - ELPP->uninstallPerformanceTrackingCallback<T>(id); - } - template <typename T> - static inline T* performanceTrackingCallback(const std::string& id) { - return ELPP->performanceTrackingCallback<T>(id); - } - /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const - template <typename T> - static std::string convertTemplateToStdString(const T& templ) { - el::Logger* logger = - ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); - if (logger == nullptr) { - return std::string(); - } - base::MessageBuilder b; - b.initialize(logger); - logger->acquireLock(); - b << templ; +/// @brief Static helpers for developers +class Helpers : base::StaticClass { + public: + /// @brief Shares logging repository (base::Storage) + static inline void setStorage(base::type::StoragePointer storage) { + ELPP = storage; + } + /// @return Main storage repository + static inline base::type::StoragePointer storage() { + return ELPP; + } + /// @brief Sets application arguments and figures out whats active for logging and whats not. + static inline void setArgs(int argc, char** argv) { + ELPP->setApplicationArguments(argc, argv); + } + /// @copydoc setArgs(int argc, char** argv) + static inline void setArgs(int argc, const char** argv) { + ELPP->setApplicationArguments(argc, const_cast<char**>(argv)); + } + /// @brief Sets thread name for current thread. Requires std::thread + static inline void setThreadName(const std::string& name) { + ELPP->setThreadName(name); + } + static inline std::string getThreadName() { + return ELPP->getThreadName(base::threading::getCurrentThreadId()); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Overrides default crash handler and installs custom handler. + /// @param crashHandler A functor with no return type that takes single int argument. + /// Handler is a typedef with specification: void (*Handler)(int) + static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { + el::elCrashHandler.setHandler(crashHandler); + } + /// @brief Abort due to crash with signal in parameter + /// @param sig Crash signal + static void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0); + /// @brief Logs reason of crash as per sig + /// @param sig Crash signal + /// @param stackTraceIfAvailable Includes stack trace if available + /// @param level Logging level + /// @param logger Logger to use for logging + static void logCrashReason(int sig, bool stackTraceIfAvailable = false, + Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out + /// (can be useful for backing up) + static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { + ELPP->setPreRollOutCallback(callback); + } + /// @brief Uninstalls pre rollout callback + static inline void uninstallPreRollOutCallback(void) { + ELPP->unsetPreRollOutCallback(); + } + /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched + template <typename T> + static inline bool installLogDispatchCallback(const std::string& id) { + return ELPP->installLogDispatchCallback<T>(id); + } + /// @brief Uninstalls log dispatch callback + template <typename T> + static inline void uninstallLogDispatchCallback(const std::string& id) { + ELPP->uninstallLogDispatchCallback<T>(id); + } + template <typename T> + static inline T* logDispatchCallback(const std::string& id) { + return ELPP->logDispatchCallback<T>(id); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished + template <typename T> + static inline bool installPerformanceTrackingCallback(const std::string& id) { + return ELPP->installPerformanceTrackingCallback<T>(id); + } + /// @brief Uninstalls post performance tracking handler + template <typename T> + static inline void uninstallPerformanceTrackingCallback(const std::string& id) { + ELPP->uninstallPerformanceTrackingCallback<T>(id); + } + template <typename T> + static inline T* performanceTrackingCallback(const std::string& id) { + return ELPP->performanceTrackingCallback<T>(id); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const + template <typename T> + static std::string convertTemplateToStdString(const T& templ) { + el::Logger* logger = + ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); + if (logger == nullptr) { + return std::string(); + } + base::MessageBuilder b; + b.initialize(logger); + logger->acquireLock(); + b << templ; #if defined(ELPP_UNICODE) - std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); + std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); #else - std::string s = logger->stream().str(); + std::string s = logger->stream().str(); #endif // defined(ELPP_UNICODE) - logger->stream().str(ELPP_LITERAL("")); - logger->releaseLock(); - return s; - } - /// @brief Returns command line arguments (pointer) provided to easylogging++ - static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { - return ELPP->commandLineArgs(); - } - /// @brief Installs user defined format specifier and handler - static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { - ELPP->installCustomFormatSpecifier(customFormatSpecifier); - } - /// @brief Uninstalls user defined format specifier and handler - static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { - return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); - } - /// @brief Returns true if custom format specifier is installed - static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { - return ELPP->hasCustomFormatSpecifier(formatSpecifier); - } - static inline void validateFileRolling(Logger* logger, Level level) { - if (logger == nullptr) return; - logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); - } - }; - /// @brief Static helpers to deal with loggers and their configurations - class Loggers : base::StaticClass { - public: - /// @brief Gets existing or registers new logger - static inline Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true) { - base::threading::ScopedLock scopedLock(ELPP->lock()); - return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); - } - /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister - /// loggers initialized / used by third-party libs. - static inline bool unregisterLogger(const std::string& identity) { - base::threading::ScopedLock scopedLock(ELPP->lock()); - return ELPP->registeredLoggers()->remove(identity); - } - /// @brief Whether or not logger with id is registered - static inline bool hasLogger(const std::string& identity) { - base::threading::ScopedLock scopedLock(ELPP->lock()); - return ELPP->registeredLoggers()->has(identity); - } - /// @brief Reconfigures specified logger with new configurations - static inline Logger* reconfigureLogger(Logger* logger, const Configurations& configurations) { - if (!logger) return nullptr; - logger->configure(configurations); - return logger; - } - /// @brief Reconfigures logger with new configurations after looking it up using identity - static inline Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations) { - return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); - } - /// @brief Reconfigures logger's single configuration - static inline Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType, - const std::string& value) { - Logger* logger = Loggers::getLogger(identity); - if (logger == nullptr) { - return nullptr; - } - logger->configurations()->set(Level::Global, configurationType, value); - logger->reconfigure(); - return logger; - } - /// @brief Reconfigures all the existing loggers with new configurations - static inline void reconfigureAllLoggers(const Configurations& configurations) { - for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); - it != ELPP->registeredLoggers()->end(); ++it) { - Loggers::reconfigureLogger(it->second, configurations); - } - } - /// @brief Reconfigures single configuration for all the loggers - static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { - reconfigureAllLoggers(Level::Global, configurationType, value); - } - /// @brief Reconfigures single configuration for all the loggers for specified level - static inline void reconfigureAllLoggers(Level level, ConfigurationType configurationType, - const std::string& value) { - for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); - it != ELPP->registeredLoggers()->end(); ++it) { - Logger* logger = it->second; - logger->configurations()->set(level, configurationType, value); - logger->reconfigure(); - } - } - /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers - static inline void setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers = false) { - ELPP->registeredLoggers()->setDefaultConfigurations(configurations); - if (reconfigureExistingLoggers) { - Loggers::reconfigureAllLoggers(configurations); - } - } - /// @brief Returns current default - static inline const Configurations* defaultConfigurations(void) { - return ELPP->registeredLoggers()->defaultConfigurations(); - } - /// @brief Returns log stream reference pointer if needed by user - static inline const base::LogStreamsReferenceMap* logStreamsReference(void) { - return ELPP->registeredLoggers()->logStreamsReference(); - } - /// @brief Default typed configuration based on existing defaultConf - static base::TypedConfigurations defaultTypedConfigurations(void) { - return base::TypedConfigurations( - ELPP->registeredLoggers()->defaultConfigurations(), - ELPP->registeredLoggers()->logStreamsReference()); - } - /// @brief Populates all logger IDs in current repository. - /// @param [out] targetList List of fill up. - static inline std::vector<std::string>* populateAllLoggerIds(std::vector<std::string>* targetList) { - targetList->clear(); - for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); - it != ELPP->registeredLoggers()->list().end(); ++it) { - targetList->push_back(it->first); - } - return targetList; - } - /// @brief Sets configurations from global configuration file. - static void configureFromGlobal(const char* globalConfigurationFilePath) { - std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in); - ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath - << "] for parsing."); - std::string line = std::string(); - std::stringstream ss; - Logger* logger = nullptr; - auto configure = [&](void) { - ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str() - << "\n--------------"); - Configurations c; - c.parseFromText(ss.str()); - logger->configure(c); - }; - while (gcfStream.good()) { - std::getline(gcfStream, line); - ELPP_INTERNAL_INFO(1, "Parsing line: " << line); - base::utils::Str::trim(line); - if (Configurations::Parser::isComment(line)) continue; - Configurations::Parser::ignoreComments(&line); - base::utils::Str::trim(line); - if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) { - if (!ss.str().empty() && logger != nullptr) { - configure(); - } - ss.str(std::string("")); - line = line.substr(2); - base::utils::Str::trim(line); - if (line.size() > 1) { - ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'"); - logger = getLogger(line); - } - } else { - ss << line << "\n"; - } - } - if (!ss.str().empty() && logger != nullptr) { - configure(); - } - } - /// @brief Configures loggers using command line arg. Ensure you have already set command line args, - /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. - /// If true is returned that does not mean it has been configured successfully, it only means that it - /// has attempeted to configure logger using configuration file provided in argument - static inline bool configureFromArg(const char* argKey) { -#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) - ELPP_UNUSED(argKey); -#else - if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) { - return false; - } - configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey)); -#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) - return true; - } - /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered - static inline void flushAll(void) { - ELPP->registeredLoggers()->flushAll(); - } - /// @brief Adds logging flag used internally. - static inline void addFlag(LoggingFlag flag) { - ELPP->addFlag(flag); - } - /// @brief Removes logging flag used internally. - static inline void removeFlag(LoggingFlag flag) { - ELPP->removeFlag(flag); - } - /// @brief Determines whether or not certain flag is active - static inline bool hasFlag(LoggingFlag flag) { - return ELPP->hasFlag(flag); - } - /// @brief Adds flag and removes it when scope goes out - class ScopedAddFlag { - public: - ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { Loggers::addFlag(m_flag); } - ~ScopedAddFlag(void) { Loggers::removeFlag(m_flag); } - private: - LoggingFlag m_flag; - }; - /// @brief Removes flag and add it when scope goes out - class ScopedRemoveFlag { - public: - ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { Loggers::removeFlag(m_flag); } - ~ScopedRemoveFlag(void) { Loggers::addFlag(m_flag); } - private: - LoggingFlag m_flag; - }; - /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) - static inline void setLoggingLevel(Level level) { - ELPP->setLoggingLevel(level); - } - /// @brief Sets verbose level on the fly - static inline void setVerboseLevel(base::type::VerboseLevel level) { - ELPP->vRegistry()->setLevel(level); - } - /// @brief Gets current verbose level - static inline base::type::VerboseLevel verboseLevel(void) { - return ELPP->vRegistry()->level(); - } - /// @brief Sets vmodules as specified (on the fly) - static inline void setVModules(const char* modules) { - if (ELPP->vRegistry()->vModulesEnabled()) { - ELPP->vRegistry()->setModules(modules); - } - } - /// @brief Sets categories as specified (on the fly) - static inline void setCategories(const char* categories, bool clear = true) { - ELPP->vRegistry()->setCategories(categories, clear); - } - /// @brief Sets thread name (to replace ID) - static inline void setThreadName(const std::string &name) { - ELPP->vRegistry()->setThreadName(name); - } - /// @brief Clears vmodules - static inline void clearVModules(void) { - ELPP->vRegistry()->clearModules(); - } - /// @brief Clears categories - static inline void clearCategories(void) { - ELPP->vRegistry()->clearCategories(); - } - /// @brief Sets filename common prefix - static inline void setFilenameCommonPrefix(const std::string &prefix) { - ELPP->vRegistry()->setFilenameCommonPrefix(prefix); - } - }; - class VersionInfo : base::StaticClass { - public: - /// @brief Current version number - static inline const std::string version(void) { return std::string("9.84"); } - /// @brief Release date of current version - static inline const std::string releaseDate(void) { return std::string("29-07-2016 1221hrs"); } - }; + logger->stream().str(ELPP_LITERAL("")); + logger->releaseLock(); + return s; + } + /// @brief Returns command line arguments (pointer) provided to easylogging++ + static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { + return ELPP->commandLineArgs(); + } + /// @brief Installs user defined format specifier and handler + static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + ELPP->installCustomFormatSpecifier(customFormatSpecifier); + } + /// @brief Uninstalls user defined format specifier and handler + static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); + } + /// @brief Returns true if custom format specifier is installed + static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->hasCustomFormatSpecifier(formatSpecifier); + } + static inline void validateFileRolling(Logger* logger, Level level) { + if (logger == nullptr) return; + logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); + } +}; +/// @brief Static helpers to deal with loggers and their configurations +class Loggers : base::StaticClass { + public: + /// @brief Gets existing or registers new logger + static Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true); + /// @brief Changes default log builder for future loggers + static void setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr); + /// @brief Installs logger registration callback, this callback is triggered when new logger is registered + template <typename T> + static inline bool installLoggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->installLoggerRegistrationCallback<T>(id); + } + /// @brief Uninstalls log dispatch callback + template <typename T> + static inline void uninstallLoggerRegistrationCallback(const std::string& id) { + ELPP->registeredLoggers()->uninstallLoggerRegistrationCallback<T>(id); + } + template <typename T> + static inline T* loggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->loggerRegistrationCallback<T>(id); + } + /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister + /// loggers initialized / used by third-party libs. + static bool unregisterLogger(const std::string& identity); + /// @brief Whether or not logger with id is registered + static bool hasLogger(const std::string& identity); + /// @brief Reconfigures specified logger with new configurations + static Logger* reconfigureLogger(Logger* logger, const Configurations& configurations); + /// @brief Reconfigures logger with new configurations after looking it up using identity + static Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations); + /// @brief Reconfigures logger's single configuration + static Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value); + /// @brief Reconfigures all the existing loggers with new configurations + static void reconfigureAllLoggers(const Configurations& configurations); + /// @brief Reconfigures single configuration for all the loggers + static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { + reconfigureAllLoggers(Level::Global, configurationType, value); + } + /// @brief Reconfigures single configuration for all the loggers for specified level + static void reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value); + /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers + static void setDefaultConfigurations(const Configurations& configurations, + bool reconfigureExistingLoggers = false); + /// @brief Returns current default + static const Configurations* defaultConfigurations(void); + /// @brief Returns log stream reference pointer if needed by user + static const base::LogStreamsReferenceMap* logStreamsReference(void); + /// @brief Default typed configuration based on existing defaultConf + static base::TypedConfigurations defaultTypedConfigurations(void); + /// @brief Populates all logger IDs in current repository. + /// @param [out] targetList List of fill up. + static std::vector<std::string>* populateAllLoggerIds(std::vector<std::string>* targetList); + /// @brief Sets configurations from global configuration file. + static void configureFromGlobal(const char* globalConfigurationFilePath); + /// @brief Configures loggers using command line arg. Ensure you have already set command line args, + /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. + /// If true is returned that does not mean it has been configured successfully, it only means that it + /// has attempeted to configure logger using configuration file provided in argument + static bool configureFromArg(const char* argKey); + /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered + static void flushAll(void); + /// @brief Adds logging flag used internally. + static inline void addFlag(LoggingFlag flag) { + ELPP->addFlag(flag); + } + /// @brief Removes logging flag used internally. + static inline void removeFlag(LoggingFlag flag) { + ELPP->removeFlag(flag); + } + /// @brief Determines whether or not certain flag is active + static inline bool hasFlag(LoggingFlag flag) { + return ELPP->hasFlag(flag); + } + /// @brief Adds flag and removes it when scope goes out + class ScopedAddFlag { + public: + ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::addFlag(m_flag); + } + ~ScopedAddFlag(void) { + Loggers::removeFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Removes flag and add it when scope goes out + class ScopedRemoveFlag { + public: + ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::removeFlag(m_flag); + } + ~ScopedRemoveFlag(void) { + Loggers::addFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) + static void setLoggingLevel(Level level) { + ELPP->setLoggingLevel(level); + } + /// @brief Sets verbose level on the fly + static void setVerboseLevel(base::type::VerboseLevel level); + /// @brief Gets current verbose level + static base::type::VerboseLevel verboseLevel(void); + /// @brief Sets vmodules as specified (on the fly) + static void setVModules(const char* modules); + /// @brief Sets categories as specified (on the fly) + static void setCategories(const char* categories, bool clear = true); + /// @brief Clears vmodules + static void clearVModules(void); + /// @brief Clears categories + static void clearCategories(void); + /// @brief Sets filename common prefix + static void setFilenameCommonPrefix(const std::string &prefix); + /// @brief Gets filename common prefix + static const std::string &getFilenameCommonPrefix(); +}; +class VersionInfo : base::StaticClass { + public: + /// @brief Current version number + static const std::string version(void); + + /// @brief Release date of current version + static const std::string releaseDate(void); +}; } // namespace el #undef VLOG_IS_ON /// @brief Determines whether verbose logging is on for specified level current file. #define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__)) #undef TIMED_BLOCK #undef TIMED_SCOPE +#undef TIMED_SCOPE_IF #undef TIMED_FUNC +#undef TIMED_FUNC_IF #undef ELPP_MIN_UNIT #if defined(ELPP_PERFORMANCE_MICROSECONDS) -# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond +# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond #else -# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond +# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond #endif // (defined(ELPP_PERFORMANCE_MICROSECONDS)) /// @brief Performance tracked scope. Performance gets written when goes out of scope using /// 'performance' logger. /// -/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint(); +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); /// @see el::base::PerformanceTracker /// @see el::base::PerformanceTracker::checkpoint // Note: Do not surround this definition with null macro because of obj instance -#define TIMED_SCOPE(obj, blockname) el::base::PerformanceTracker obj(blockname, ELPP_MIN_UNIT) -#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::PerformanceTracker timer; } obj = { 0, \ -el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i) +#define TIMED_SCOPE_IF(obj, blockname, condition) el::base::type::PerformanceTrackerPtr obj( condition ? \ + new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr ) +#define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true) +#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::type::PerformanceTrackerPtr timer; } obj = { 0, \ + el::base::type::PerformanceTrackerPtr(new el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT)) }; obj.i < 1; ++obj.i) /// @brief Performance tracked function. Performance gets written when goes out of scope using /// 'performance' logger. /// -/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint(); +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); /// @see el::base::PerformanceTracker /// @see el::base::PerformanceTracker::checkpoint +#define TIMED_FUNC_IF(obj,condition) TIMED_SCOPE_IF(obj, ELPP_FUNC, condition) #define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC) #undef PERFORMANCE_CHECKPOINT #undef PERFORMANCE_CHECKPOINT_WITH_ID -#define PERFORMANCE_CHECKPOINT(obj) obj.checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) -#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj.checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT(obj) obj->checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj->checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) #undef ELPP_COUNTER #undef ELPP_COUNTER_POS /// @brief Gets hit counter for file/line @@ -6281,212 +4057,212 @@ el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i) #undef CVERBOSE_N_TIMES // Normal logs #if ELPP_INFO_LOG -# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) +# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) #else -# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() +# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_INFO_LOG #if ELPP_WARNING_LOG -# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) +# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) #else -# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() +# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_WARNING_LOG #if ELPP_DEBUG_LOG -# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) +# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) #else -# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() +# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_DEBUG_LOG #if ELPP_ERROR_LOG -# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) +# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) #else -# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() +# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_ERROR_LOG #if ELPP_FATAL_LOG -# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) +# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) #else -# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() +# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_FATAL_LOG #if ELPP_TRACE_LOG -# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) +# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) #else -# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() +# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_TRACE_LOG #if ELPP_VERBOSE_LOG -# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ +# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) #else -# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() +# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_VERBOSE_LOG // Conditional logs #if ELPP_INFO_LOG -# define CINFO_IF(writer, condition_, dispatchAction, ...) \ +# define CINFO_IF(writer, condition_, dispatchAction, ...) \ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__) #else -# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_INFO_LOG #if ELPP_WARNING_LOG -# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ +# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__) #else -# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_WARNING_LOG #if ELPP_DEBUG_LOG -# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ +# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__) #else -# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_DEBUG_LOG #if ELPP_ERROR_LOG -# define CERROR_IF(writer, condition_, dispatchAction, ...)\ +# define CERROR_IF(writer, condition_, dispatchAction, ...)\ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__) #else -# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_ERROR_LOG #if ELPP_FATAL_LOG -# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ +# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__) #else -# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_FATAL_LOG #if ELPP_TRACE_LOG -# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ +# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__) #else -# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_TRACE_LOG #if ELPP_VERBOSE_LOG -# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) #else -# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_VERBOSE_LOG // Occasional logs #if ELPP_INFO_LOG -# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__) #else -# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_INFO_LOG #if ELPP_WARNING_LOG -# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__) #else -# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_WARNING_LOG #if ELPP_DEBUG_LOG -# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__) #else -# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_DEBUG_LOG #if ELPP_ERROR_LOG -# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__) #else -# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_ERROR_LOG #if ELPP_FATAL_LOG -# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__) #else -# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_FATAL_LOG #if ELPP_TRACE_LOG -# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__) #else -# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_TRACE_LOG #if ELPP_VERBOSE_LOG -# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__) #else -# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_VERBOSE_LOG // After N logs #if ELPP_INFO_LOG -# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ +# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) #else -# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_INFO_LOG #if ELPP_WARNING_LOG -# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) #else -# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_WARNING_LOG #if ELPP_DEBUG_LOG -# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) #else -# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_DEBUG_LOG #if ELPP_ERROR_LOG -# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ +# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) #else -# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_ERROR_LOG #if ELPP_FATAL_LOG -# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) #else -# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_FATAL_LOG #if ELPP_TRACE_LOG -# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) #else -# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_TRACE_LOG #if ELPP_VERBOSE_LOG -# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) #else -# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_VERBOSE_LOG // N Times logs #if ELPP_INFO_LOG -# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ +# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) #else -# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_INFO_LOG #if ELPP_WARNING_LOG -# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) #else -# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_WARNING_LOG #if ELPP_DEBUG_LOG -# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) #else -# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_DEBUG_LOG #if ELPP_ERROR_LOG -# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ +# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) #else -# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_ERROR_LOG #if ELPP_FATAL_LOG -# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) #else -# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_FATAL_LOG #if ELPP_TRACE_LOG -# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) #else -# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_TRACE_LOG #if ELPP_VERBOSE_LOG -# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) #else -# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() #endif // ELPP_VERBOSE_LOG // // Custom Loggers - Requires (level, dispatchAction, loggerId/s) @@ -6542,9 +4318,9 @@ CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLo #undef VLOG_N_TIMES #undef ELPP_CURR_FILE_LOGGER_ID #if defined(ELPP_DEFAULT_LOGGER) -# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER +# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER #else -# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId +# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId #endif #undef ELPP_TRACE #define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID) @@ -6604,53 +4380,53 @@ C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base: #undef DSYSLOG_AFTER_N #undef DSYSLOG_N_TIMES #if defined(ELPP_SYSLOG) -# define CSYSLOG(LEVEL, ...)\ +# define CSYSLOG(LEVEL, ...)\ C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define CSYSLOG_IF(condition, LEVEL, ...)\ +# define CSYSLOG_IF(condition, LEVEL, ...)\ C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) -# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) -# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) -# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define DCSYSLOG_IF(condition, LEVEL, ...)\ +# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_IF(condition, LEVEL, ...)\ C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__) -# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ +# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ +# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ +# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) -# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) -# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) #else -# define CSYSLOG(LEVEL, ...) el::base::NullWriter() -# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() -# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() -# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() -# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() -# define SYSLOG(LEVEL) el::base::NullWriter() -# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() -# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() -# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() -# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() -# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() -# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() -# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() -# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() -# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() -# define DSYSLOG(LEVEL) el::base::NullWriter() -# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() -# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() -# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() -# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define CSYSLOG(LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define SYSLOG(LEVEL) el::base::NullWriter() +# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define DSYSLOG(LEVEL) el::base::NullWriter() +# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() #endif // defined(ELPP_SYSLOG) // // Custom Debug Only Loggers - Requires (level, loggerId/s) @@ -6683,6 +4459,7 @@ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAc // // Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros // +#if !defined(ELPP_NO_DEBUG_MACROS) // undef existing #undef DLOG #undef DVLOG @@ -6707,8 +4484,9 @@ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAc #define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) #define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) #define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#endif // defined(ELPP_NO_DEBUG_MACROS) +#if !defined(ELPP_NO_CHECK_MACROS) // Check macros -#if !defined(ELPP_DISABLE_CHECK_MACROS) #undef CCHECK #undef CPCHECK #undef CCHECK_EQ @@ -6817,15 +4595,14 @@ if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAc #define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) #define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) #define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) -#endif // define(ELPP_DISABLE_CHECK_MACROS) +#endif // defined(ELPP_NO_CHECK_MACROS) #if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) -# define ELPP_USE_DEF_CRASH_HANDLER false +# define ELPP_USE_DEF_CRASH_HANDLER false #else -# define ELPP_USE_DEF_CRASH_HANDLER true +# define ELPP_USE_DEF_CRASH_HANDLER true #endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) #define ELPP_CRASH_HANDLER_INIT #define ELPP_INIT_EASYLOGGINGPP(val) \ -ELPP_INITI_BASIC_DECLR \ namespace el { \ namespace base { \ el::base::type::StoragePointer elStorage(val); \ @@ -6834,22 +4611,18 @@ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ } #if ELPP_ASYNC_LOGGING -# define INITIALIZE_EASYLOGGINGPP\ -ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ -new el::base::AsyncDispatchWorker()))\ - +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ +new el::base::AsyncDispatchWorker())) #else -# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) #endif // ELPP_ASYNC_LOGGING -#define INITIALIZE_NULL_EASYLOGGINGPP\ -ELPP_INITI_BASIC_DECLR\ +#define INITIALIZE_NULL_EASYLOGGINGPP \ namespace el {\ namespace base {\ el::base::type::StoragePointer elStorage;\ }\ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ } -// NOTE: no ELPP_INITI_BASIC_DECLR when sharing - causes double free corruption on external symbols #define SHARE_EASYLOGGINGPP(initializedStorage)\ namespace el {\ namespace base {\ @@ -6859,8 +4632,8 @@ el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ } #if defined(ELPP_UNICODE) -# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) #else -# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) #endif // defined(ELPP_UNICODE) #endif // EASYLOGGINGPP_H diff --git a/external/miniupnpc/testdesc/new_LiveBox_desc.xml b/external/miniupnpc/testdesc/new_LiveBox_desc.xml index 620eb55af..9d5160bb8 100644 --- a/external/miniupnpc/testdesc/new_LiveBox_desc.xml +++ b/external/miniupnpc/testdesc/new_LiveBox_desc.xml @@ -87,4 +87,4 @@ </device>
</deviceList>
</device>
-</root>
\ No newline at end of file +</root> diff --git a/external/unbound/CMakeLists.txt b/external/unbound/CMakeLists.txt index c1e2d6887..94294013f 100644 --- a/external/unbound/CMakeLists.txt +++ b/external/unbound/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -30,22 +30,6 @@ cmake_minimum_required(VERSION 2.8.7) project(unbound C) -if (APPLE) - if (NOT OpenSSL_DIR) - EXECUTE_PROCESS(COMMAND brew --prefix openssl - OUTPUT_VARIABLE OPENSSL_ROOT_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "Using OpenSSL found at ${OPENSSL_ROOT_DIR}") - endif() -endif() - -find_package(OpenSSL REQUIRED) -if(STATIC) - if(UNIX) - set(OPENSSL_LIBRARIES "${OPENSSL_LIBRARIES};${CMAKE_DL_LIBS}") - endif() -endif() - find_package(Threads) include(configure_checks.cmake) @@ -231,6 +215,11 @@ endif () if (INSTALL_VENDORED_LIBUNBOUND) + if(IOS) + set(lib_folder lib-${ARCH}) + else() + set(lib_folder lib) + endif() install(TARGETS unbound - ARCHIVE DESTINATION lib) + ARCHIVE DESTINATION ${lib_folder}) endif() diff --git a/external/unbound/validator/val_secalgo.c b/external/unbound/validator/val_secalgo.c index 7c8d7b287..256c3ff92 100644 --- a/external/unbound/validator/val_secalgo.c +++ b/external/unbound/validator/val_secalgo.c @@ -345,7 +345,11 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, "EVP_PKEY_assign_DSA failed"); return 0; } +#ifdef HAVE_EVP_DSS1 *digest_type = EVP_dss1(); +#else + *digest_type = EVP_sha1(); +#endif break; case LDNS_RSASHA1: diff --git a/include/INode.h b/include/INode.h index f3dc5cb0a..e4a9def06 100644 --- a/include/INode.h +++ b/include/INode.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/include/IWallet.h b/include/IWallet.h index f39ceba1f..862cdcd35 100644 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -0,0 +1 @@ +contrib/snap
\ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc7d60e4c..d83242a3c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -99,19 +99,26 @@ endfunction () add_subdirectory(common) add_subdirectory(crypto) add_subdirectory(ringct) +add_subdirectory(cryptonote_basic) add_subdirectory(cryptonote_core) -add_subdirectory(blockchain_db) +if(NOT IOS) + add_subdirectory(blockchain_db) +endif() add_subdirectory(mnemonics) -add_subdirectory(rpc) +if(NOT IOS) + add_subdirectory(rpc) +endif() add_subdirectory(wallet) -add_subdirectory(p2p) +if(NOT IOS) + add_subdirectory(p2p) +endif() add_subdirectory(cryptonote_protocol) - -add_subdirectory(simplewallet) -add_subdirectory(daemonizer) -add_subdirectory(daemon) - -add_subdirectory(blockchain_utilities) +if(NOT IOS) + add_subdirectory(simplewallet) + add_subdirectory(daemonizer) + add_subdirectory(daemon) + add_subdirectory(blockchain_utilities) +endif() if(PER_BLOCK_CHECKPOINT) add_subdirectory(blocks) diff --git a/src/blockchain_db/CMakeLists.txt b/src/blockchain_db/CMakeLists.txt index cefe93ebe..c27aa73b0 100644 --- a/src/blockchain_db/CMakeLists.txt +++ b/src/blockchain_db/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -63,6 +63,7 @@ target_link_libraries(blockchain_db PUBLIC common crypto + ringct ${LMDB_LIBRARY} ${BDB_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index 57d8371bd..c954a7751 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -31,7 +31,7 @@ #include <memory> // std::unique_ptr #include <cstring> // memcpy -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "crypto/crypto.h" #include "profile_tools.h" diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index 266e780c6..a040a70ef 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index bd1a38ec3..136d4fa80 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ #include <boost/range/adaptor/reversed.hpp> #include "blockchain_db.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "profile_tools.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -130,12 +130,6 @@ uint64_t BlockchainDB::add_block( const block& blk uint64_t prev_height = height(); - // call out to subclass implementation to add the block & metadata - time1 = epee::misc_utils::get_tick_count(); - add_block(blk, block_size, cumulative_difficulty, coins_generated, blk_hash); - TIME_MEASURE_FINISH(time1); - time_add_block1 += time1; - // call out to add the transactions time1 = epee::misc_utils::get_tick_count(); @@ -151,6 +145,12 @@ uint64_t BlockchainDB::add_block( const block& blk TIME_MEASURE_FINISH(time1); time_add_transaction += time1; + // call out to subclass implementation to add the block & metadata + time1 = epee::misc_utils::get_tick_count(); + add_block(blk, block_size, cumulative_difficulty, coins_generated, blk_hash); + TIME_MEASURE_FINISH(time1); + time_add_block1 += time1; + m_hardfork->add(blk, prev_height); block_txn_stop(); @@ -200,6 +200,45 @@ void BlockchainDB::remove_transaction(const crypto::hash& tx_hash) remove_transaction_data(tx_hash, tx); } +block BlockchainDB::get_block_from_height(const uint64_t& height) const +{ + blobdata bd = get_block_blob_from_height(height); + block b; + if (!parse_and_validate_block_from_blob(bd, b)) + throw new DB_ERROR("Failed to parse block from blob retrieved from the db"); + + return b; +} + +block BlockchainDB::get_block(const crypto::hash& h) const +{ + blobdata bd = get_block_blob(h); + block b; + if (!parse_and_validate_block_from_blob(bd, b)) + throw new DB_ERROR("Failed to parse block from blob retrieved from the db"); + + return b; +} + +bool BlockchainDB::get_tx(const crypto::hash& h, cryptonote::transaction &tx) const +{ + blobdata bd; + if (!get_tx_blob(h, bd)) + return false; + if (!parse_and_validate_tx_from_blob(bd, tx)) + throw new DB_ERROR("Failed to parse transaction from blob retrieved from the db"); + + return true; +} + +transaction BlockchainDB::get_tx(const crypto::hash& h) const +{ + transaction tx; + if (!get_tx(h, tx)) + throw new TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str()); + return tx; +} + void BlockchainDB::reset_stats() { num_calls = 0; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 455e0c811..f5710550b 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,9 +34,10 @@ #include <string> #include <exception> #include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/difficulty.h" -#include "cryptonote_core/hardfork.h" +#include "cryptonote_protocol/blobdatatype.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/difficulty.h" +#include "cryptonote_basic/hardfork.h" /** \file * Cryptonote Blockchain Database Interface @@ -754,7 +755,20 @@ public: * * @return the block requested */ - virtual block get_block(const crypto::hash& h) const = 0; + virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const = 0; + + /** + * @brief fetches the block with the given hash + * + * Returns the requested block. + * + * If the block does not exist, the subclass should throw BLOCK_DNE + * + * @param h the hash to look for + * + * @return the block requested + */ + virtual block get_block(const crypto::hash& h) const; /** * @brief gets the height of the block with a given hash @@ -784,7 +798,7 @@ public: virtual block_header get_block_header(const crypto::hash& h) const = 0; /** - * @brief fetch a block by height + * @brief fetch a block blob by height * * The subclass should return the block at the given height. * @@ -793,9 +807,21 @@ public: * * @param height the height to look for * + * @return the block blob + */ + virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const = 0; + + /** + * @brief fetch a block by height + * + * If the block does not exist, that is to say if the blockchain is not + * that high, then the subclass should throw BLOCK_DNE + * + * @param height the height to look for + * * @return the block */ - virtual block get_block_from_height(const uint64_t& height) const = 0; + virtual block get_block_from_height(const uint64_t& height) const; /** * @brief fetch a block's timestamp @@ -1009,20 +1035,28 @@ public: /** * @brief fetches the transaction with the given hash * - * The subclass should return the transaction stored which has the given - * hash. - * * If the transaction does not exist, the subclass should throw TX_DNE. * * @param h the hash to look for * * @return the transaction with the given hash */ - virtual transaction get_tx(const crypto::hash& h) const = 0; + virtual transaction get_tx(const crypto::hash& h) const; /** * @brief fetches the transaction with the given hash * + * If the transaction does not exist, the subclass should return false. + * + * @param h the hash to look for + * + * @return true iff the transaction was found + */ + virtual bool get_tx(const crypto::hash& h, transaction &tx) const; + + /** + * @brief fetches the transaction blob with the given hash + * * The subclass should return the transaction stored which has the given * hash. * @@ -1032,7 +1066,7 @@ public: * * @return true iff the transaction was found */ - virtual bool get_tx(const crypto::hash& h, transaction &tx) const = 0; + virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const = 0; /** * @brief fetches the total number of transactions ever @@ -1184,7 +1218,7 @@ public: * @param offsets a list of amount-specific output indices * @param outputs return-by-reference a list of outputs' metadata */ - virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) = 0; + virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) = 0; /* * FIXME: Need to check with git blame and ask what this does to diff --git a/src/blockchain_db/db_types.h b/src/blockchain_db/db_types.h index 67afe0405..6c21b029e 100644 --- a/src/blockchain_db/db_types.h +++ b/src/blockchain_db/db_types.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 1cfcf3a9e..dab09e8f4 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -34,7 +34,7 @@ #include <cstring> // memcpy #include <random> -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "crypto/crypto.h" #include "profile_tools.h" @@ -377,7 +377,50 @@ void mdb_txn_safe::allow_new_txns() creation_gate.clear(); } +void lmdb_resized(MDB_env *env) +{ + mdb_txn_safe::prevent_new_txns(); + + MGINFO("LMDB map resize detected."); + + MDB_envinfo mei; + + mdb_env_info(env, &mei); + uint64_t old = mei.me_mapsize; + + mdb_txn_safe::wait_no_active_txns(); + + int result = mdb_env_set_mapsize(env, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to set new mapsize: ", result).c_str())); + + mdb_env_info(env, &mei); + uint64_t new_mapsize = mei.me_mapsize; + + MGINFO("LMDB Mapsize increased." << " Old: " << old / (1024 * 1024) << "MiB" << ", New: " << new_mapsize / (1024 * 1024) << "MiB"); + + mdb_txn_safe::allow_new_txns(); +} +inline int lmdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn) +{ + int res = mdb_txn_begin(env, parent, flags, txn); + if (res == MDB_MAP_RESIZED) { + lmdb_resized(env); + res = mdb_txn_begin(env, parent, flags, txn); + } + return res; +} + +inline int lmdb_txn_renew(MDB_txn *txn) +{ + int res = mdb_txn_renew(txn); + if (res == MDB_MAP_RESIZED) { + lmdb_resized(mdb_txn_env(txn)); + res = mdb_txn_renew(txn); + } + return res; +} void BlockchainLMDB::do_resize(uint64_t increase_size) { @@ -561,7 +604,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con { LOG_PRINT_L1("No existing blocks to check for average block size"); } - else if (m_cum_count) + else if (m_cum_count >= num_prev_blocks) { avg_block_size = m_cum_size / m_cum_count; LOG_PRINT_L1("average block size across recent " << m_cum_count << " blocks: " << avg_block_size); @@ -570,6 +613,9 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con } else { + MDB_txn *rtxn; + mdb_txn_cursors *rcurs; + block_rtxn_start(&rtxn, &rcurs); for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num) { uint32_t block_size = get_block_size(block_num); @@ -578,6 +624,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con // some blocks were to be skipped for being outliers. ++num_blocks_used; } + block_rtxn_stop(); avg_block_size = total_block_size / num_blocks_used; LOG_PRINT_L1("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size); } @@ -628,6 +675,7 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const CURSOR(blocks) CURSOR(block_info) + // this call to mdb_cursor_put will change height() MDB_val_copy<blobdata> blob(block_to_blob(blk)); result = mdb_cursor_put(m_cur_blocks, &key, &blob, MDB_APPEND); if (result) @@ -701,7 +749,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons uint64_t m_height = height(); int result; - uint64_t tx_id = m_num_txs; + uint64_t tx_id = get_tx_count(); CURSOR(txs) CURSOR(tx_indices) @@ -734,7 +782,6 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons if (result) throw0(DB_ERROR(lmdb_error("Failed to add tx blob to db transaction: ", result).c_str())); - m_num_txs++; return tx_id; } @@ -782,8 +829,6 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const // Don't delete the tx_indices entry until the end, after we're done with val_tx_id if (mdb_cursor_del(m_cur_tx_indices, 0)) throw1(DB_ERROR("Failed to add removal of tx index to db transaction")); - - m_num_txs--; } uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash, @@ -796,6 +841,7 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash, check_open(); mdb_txn_cursors *m_cursors = &m_wcursors; uint64_t m_height = height(); + uint64_t m_num_outputs = num_outputs(); int result = 0; @@ -848,7 +894,6 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash, if ((result = mdb_cursor_put(m_cur_output_amounts, &val_amount, &data, MDB_APPENDDUP))) throw0(DB_ERROR(lmdb_error("Failed to add output pubkey to db transaction: ", result).c_str())); - m_num_outputs++; return ok.amount_index; } @@ -933,8 +978,6 @@ void BlockchainLMDB::remove_output(const uint64_t amount, const uint64_t& out_in result = mdb_cursor_del(m_cur_output_amounts, 0); if (result) throw0(DB_ERROR(lmdb_error(std::string("Error deleting amount for output index ").append(boost::lexical_cast<std::string>(out_index).append(": ")).c_str(), result).c_str())); - - m_num_outputs--; } void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image) @@ -999,7 +1042,7 @@ tx_out BlockchainLMDB::output_from_blob(const blobdata& blob) const void BlockchainLMDB::check_open() const { - LOG_PRINT_L3("BlockchainLMDB::" << __func__); +// LOG_PRINT_L3("BlockchainLMDB::" << __func__); if (!m_open) throw0(DB_ERROR("DB operation attempted on a not-open DB instance")); } @@ -1154,16 +1197,6 @@ void BlockchainLMDB::open(const std::string& filename, const int mdb_flags) LOG_PRINT_L2("Setting m_height to: " << db_stats.ms_entries); uint64_t m_height = db_stats.ms_entries; - // get and keep current number of txs - if ((result = mdb_stat(txn, m_txs, &db_stats))) - throw0(DB_ERROR(lmdb_error("Failed to query m_txs: ", result).c_str())); - m_num_txs = db_stats.ms_entries; - - // get and keep current number of outputs - if ((result = mdb_stat(txn, m_output_txs, &db_stats))) - throw0(DB_ERROR(lmdb_error("Failed to query m_output_txs: ", result).c_str())); - m_num_outputs = db_stats.ms_entries; - bool compatible = true; MDB_val_copy<const char*> k("version"); @@ -1269,7 +1302,7 @@ void BlockchainLMDB::reset() check_open(); mdb_txn_safe txn; - if (auto result = mdb_txn_begin(m_env, NULL, 0, txn)) + if (auto result = lmdb_txn_begin(m_env, NULL, 0, txn)) throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); if (auto result = mdb_drop(txn, m_blocks, 0)) @@ -1303,7 +1336,6 @@ void BlockchainLMDB::reset() throw0(DB_ERROR(lmdb_error("Failed to write version to database: ", result).c_str())); txn.commit(); - m_num_outputs = 0; m_cum_size = 0; m_cum_count = 0; } @@ -1353,7 +1385,7 @@ void BlockchainLMDB::unlock() txn_ptr = m_write_txn; \ else \ { \ - if (auto mdb_res = mdb_txn_begin(m_env, NULL, flags, auto_txn)) \ + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, flags, auto_txn)) \ throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \ } \ @@ -1387,7 +1419,7 @@ void BlockchainLMDB::unlock() txn_ptr = m_write_txn; \ else \ { \ - if (auto mdb_res = mdb_txn_begin(m_env, NULL, flags, auto_txn)) \ + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, flags, auto_txn)) \ throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \ } \ @@ -1428,12 +1460,12 @@ bool BlockchainLMDB::block_exists(const crypto::hash& h, uint64_t *height) const return ret; } -block BlockchainLMDB::get_block(const crypto::hash& h) const +cryptonote::blobdata BlockchainLMDB::get_block_blob(const crypto::hash& h) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); - return get_block_from_height(get_block_height(h)); + return get_block_blob_from_height(get_block_height(h)); } uint64_t BlockchainLMDB::get_block_height(const crypto::hash& h) const @@ -1466,7 +1498,7 @@ block_header BlockchainLMDB::get_block_header(const crypto::hash& h) const return get_block(h); } -block BlockchainLMDB::get_block_from_height(const uint64_t& height) const +cryptonote::blobdata BlockchainLMDB::get_block_blob_from_height(const uint64_t& height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1487,13 +1519,9 @@ block BlockchainLMDB::get_block_from_height(const uint64_t& height) const blobdata bd; bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size); - block b; - if (!parse_and_validate_block_from_blob(bd, b)) - throw0(DB_ERROR("Failed to parse block from blob retrieved from the db")); - TXN_POSTFIX_RDONLY(); - return b; + return bd; } uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const @@ -1713,6 +1741,20 @@ uint64_t BlockchainLMDB::height() const return db_stats.ms_entries; } +uint64_t BlockchainLMDB::num_outputs() const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + TXN_PREFIX_RDONLY(); + int result; + + // get current height + MDB_stat db_stats; + if ((result = mdb_stat(m_txn, m_output_txs, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_output_txs: ", result).c_str())); + return db_stats.ms_entries; +} + bool BlockchainLMDB::tx_exists(const crypto::hash& h) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -1808,7 +1850,7 @@ uint64_t BlockchainLMDB::get_tx_unlock_time(const crypto::hash& h) const return ret; } -bool BlockchainLMDB::get_tx(const crypto::hash& h, transaction &tx) const +bool BlockchainLMDB::get_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -1831,36 +1873,24 @@ bool BlockchainLMDB::get_tx(const crypto::hash& h, transaction &tx) const else if (get_result) throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx from hash", get_result).c_str())); - blobdata bd; bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size); - if (!parse_and_validate_tx_from_blob(bd, tx)) - throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db")); - TXN_POSTFIX_RDONLY(); return true; } -transaction BlockchainLMDB::get_tx(const crypto::hash& h) const -{ - transaction tx; - - if (!get_tx(h, tx)) - throw1(TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str())); - return tx; -} - uint64_t BlockchainLMDB::get_tx_count() const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); TXN_PREFIX_RDONLY(); + int result; MDB_stat db_stats; - if (mdb_stat(m_txn, m_tx_indices, &db_stats)) - throw0(DB_ERROR("Failed to query m_tx_indices")); + if ((result = mdb_stat(m_txn, m_txs, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_txs: ", result).c_str())); TXN_POSTFIX_RDONLY(); @@ -2278,7 +2308,7 @@ bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks) m_write_batch_txn = new mdb_txn_safe(); // NOTE: need to make sure it's destroyed properly when done - if (auto mdb_res = mdb_txn_begin(m_env, NULL, 0, *m_write_batch_txn)) + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, 0, *m_write_batch_txn)) { delete m_write_batch_txn; m_write_batch_txn = nullptr; @@ -2397,12 +2427,12 @@ bool BlockchainLMDB::block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) co m_tinfo.reset(new mdb_threadinfo); memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors)); memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); - if (auto mdb_res = mdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn)) + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn)) throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str())); ret = true; } else if (!m_tinfo->m_ti_rflags.m_rf_txn) { - if (auto mdb_res = mdb_txn_renew(m_tinfo->m_ti_rtxn)) + if (auto mdb_res = lmdb_txn_renew(m_tinfo->m_ti_rtxn)) throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str())); ret = true; } @@ -2435,12 +2465,12 @@ void BlockchainLMDB::block_txn_start(bool readonly) m_tinfo.reset(new mdb_threadinfo); memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors)); memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); - if (auto mdb_res = mdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn)) + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn)) throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str())); didit = true; } else if (!m_tinfo->m_ti_rflags.m_rf_txn) { - if (auto mdb_res = mdb_txn_renew(m_tinfo->m_ti_rtxn)) + if (auto mdb_res = lmdb_txn_renew(m_tinfo->m_ti_rtxn)) throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str())); didit = true; } @@ -2466,14 +2496,15 @@ void BlockchainLMDB::block_txn_start(bool readonly) { m_writer = boost::this_thread::get_id(); m_write_txn = new mdb_txn_safe(); - if (auto mdb_res = mdb_txn_begin(m_env, NULL, 0, *m_write_txn)) + if (auto mdb_res = lmdb_txn_begin(m_env, NULL, 0, *m_write_txn)) { delete m_write_txn; m_write_txn = nullptr; throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a transaction for the db: ", mdb_res).c_str())); } memset(&m_wcursors, 0, sizeof(m_wcursors)); - } + } else if (m_writer != boost::this_thread::get_id()) + throw0(DB_ERROR_TXN_START((std::string("Attempted to start new write txn when batch txn already exists in ")+__FUNCTION__).c_str())); } void BlockchainLMDB::block_txn_stop() @@ -2544,8 +2575,6 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c } } - uint64_t num_txs = m_num_txs; - uint64_t num_outputs = m_num_outputs; try { BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs); @@ -2556,8 +2585,6 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c } catch (...) { - m_num_txs = num_txs; - m_num_outputs = num_outputs; block_txn_abort(); throw; } @@ -2572,8 +2599,6 @@ void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs) block_txn_start(false); - uint64_t num_txs = m_num_txs; - uint64_t num_outputs = m_num_outputs; try { BlockchainDB::pop_block(blk, txs); @@ -2581,8 +2606,6 @@ void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs) } catch (...) { - m_num_txs = num_txs; - m_num_outputs = num_outputs; block_txn_abort(); throw; } @@ -2616,7 +2639,7 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6 TXN_POSTFIX_RDONLY(); } -void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) +void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); TIME_MEASURE_START(db3); @@ -2634,7 +2657,14 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<ui auto get_result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_BOTH); if (get_result == MDB_NOTFOUND) + { + if (allow_partial) + { + MDEBUG("Partial result: " << outputs.size() << "/" << offsets.size()); + break; + } throw1(OUTPUT_DNE((std::string("Attempting to get output pubkey by global index (amount ") + boost::lexical_cast<std::string>(amount) + ", index " + boost::lexical_cast<std::string>(index) + ", count " + boost::lexical_cast<std::string>(get_num_outputs(amount)) + "), but key does not exist").c_str())); + } else if (get_result) throw0(DB_ERROR(lmdb_error("Error attempting to retrieve an output pubkey from the db", get_result).c_str())); @@ -3256,8 +3286,6 @@ void BlockchainLMDB::migrate_0_1() lmdb_db_open(txn, LMDB_OUTPUT_AMOUNTS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_output_amounts, "Failed to open db handle for m_output_amounts"); mdb_set_dupsort(txn, m_output_amounts, compare_uint64); txn.commit(); - m_num_txs = 0; - m_num_outputs = 0; } while(0); do { @@ -3328,12 +3356,9 @@ void BlockchainLMDB::migrate_0_1() throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs: ", result).c_str())); if (!i) { MDB_stat ms; - mdb_stat(txn, m_output_txs, &ms); - m_num_outputs = ms.ms_entries; mdb_stat(txn, m_txs, &ms); - m_num_txs = i = ms.ms_entries; + i = ms.ms_entries; if (i) { - m_num_txs = i; MDB_val_set(pk, "txblk"); result = mdb_cursor_get(c_props, &pk, &k, MDB_SET); if (result) diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index e7faf8cdc..8a5677566 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -170,13 +170,13 @@ public: virtual bool block_exists(const crypto::hash& h, uint64_t *height = NULL) const; - virtual block get_block(const crypto::hash& h) const; - virtual uint64_t get_block_height(const crypto::hash& h) const; virtual block_header get_block_header(const crypto::hash& h) const; - virtual block get_block_from_height(const uint64_t& height) const; + virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const; + + virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const; virtual uint64_t get_block_timestamp(const uint64_t& height) const; @@ -207,9 +207,7 @@ public: virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const; - virtual transaction get_tx(const crypto::hash& h) const; - - virtual bool get_tx(const crypto::hash& h, transaction &tx) const; + virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; virtual uint64_t get_tx_count() const; @@ -221,7 +219,7 @@ public: virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index); virtual output_data_t get_output_key(const uint64_t& global_index) const; - virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs); + virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false); virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const; virtual void get_output_tx_and_index_from_global(const std::vector<uint64_t> &global_indices, @@ -312,6 +310,8 @@ private: virtual void remove_spent_key(const crypto::key_image& k_image); + uint64_t num_outputs() const; + // Hard fork virtual void set_hard_fork_version(uint64_t height, uint8_t version); virtual uint8_t get_hard_fork_version(uint64_t height) const; @@ -369,10 +369,8 @@ private: MDB_dbi m_properties; - uint64_t m_num_txs; - uint64_t m_num_outputs; mutable uint64_t m_cum_size; // used in batch size estimation - mutable int m_cum_count; + mutable unsigned int m_cum_count; std::string m_folder; mdb_txn_safe* m_write_txn; // may point to either a short-lived txn or a batch txn mdb_txn_safe* m_write_batch_txn; // persist batch txn outside of BlockchainLMDB diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index b26fe04cc..bb23cdc8b 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -119,6 +119,7 @@ monero_add_executable(cn_deserialize target_link_libraries(cn_deserialize LINK_PRIVATE cryptonote_core + blockchain_db p2p epee ${CMAKE_THREAD_LIBS_INIT}) diff --git a/src/blockchain_utilities/README.md b/src/blockchain_utilities/README.md index 809df3bfa..d18dcba8c 100644 --- a/src/blockchain_utilities/README.md +++ b/src/blockchain_utilities/README.md @@ -1,6 +1,6 @@ # Monero Blockchain Utilities -Copyright (c) 2014-2016, The Monero Project +Copyright (c) 2014-2017, The Monero Project ## Introduction diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index b4411fbba..f145bc107 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -57,6 +57,10 @@ std::string join_set_strings(const std::unordered_set<std::string>& db_types_all int main(int argc, char* argv[]) { + TRY_ENTRY(); + + epee::string_tools::set_module_name_and_folder(argv[0]); + std::string default_db_type = "lmdb"; std::unordered_set<std::string> db_types_all = cryptonote::blockchain_db_types; @@ -78,7 +82,7 @@ int main(int argc, char* argv[]) po::options_description desc_cmd_only("Command line options"); po::options_description desc_cmd_sett("Command line options and settings options"); const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true}; - const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", log_level}; + const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop}; const command_line::arg_descriptor<bool> arg_testnet_on = { "testnet" @@ -122,10 +126,13 @@ int main(int argc, char* argv[]) return 1; } - log_level = command_line::get_arg(vm, arg_log_level); + mlog_configure(mlog_get_default_log_path("monero-blockchain-export.log"), true); + if (!vm["log-level"].defaulted()) + mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); + else + mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str()); block_stop = command_line::get_arg(vm, arg_block_stop); - mlog_configure("monero-blockchain-export", true); LOG_PRINT_L0("Starting..."); bool opt_testnet = command_line::get_arg(vm, arg_testnet_on); @@ -223,4 +230,7 @@ int main(int argc, char* argv[]) } CHECK_AND_ASSERT_MES(r, false, "Failed to export blockchain raw data"); LOG_PRINT_L0("Blockchain raw data exported OK"); + return 0; + + CATCH_ENTRY("Export error", 1); } diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index f21673b89..b2c217ca1 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,7 +34,7 @@ #include <boost/filesystem.hpp> #include "bootstrap_file.h" #include "bootstrap_serialization.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "serialization/binary_utils.h" // dump_binary(), parse_binary() #include "serialization/json_utils.h" // dump_json() #include "include_base_utils.h" @@ -374,7 +374,7 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path, return 2; } bytes_read += chunk_size; - MINFO("Total bytes read: " << bytes_read); + MDEBUG("Total bytes read: " << bytes_read); if (h + NUM_BLOCKS_PER_CHUNK < start_height + 1) { @@ -596,6 +596,10 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path, int main(int argc, char* argv[]) { + TRY_ENTRY(); + + epee::string_tools::set_module_name_and_folder(argv[0]); + std::string default_db_type = "lmdb"; std::string default_db_engine_compiled = "blockchain_db"; @@ -620,7 +624,7 @@ int main(int argc, char* argv[]) po::options_description desc_cmd_only("Command line options"); po::options_description desc_cmd_sett("Command line options and settings options"); const command_line::arg_descriptor<std::string> arg_input_file = {"input-file", "Specify input file", "", true}; - const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", log_level}; + const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop}; const command_line::arg_descriptor<uint64_t> arg_batch_size = {"batch-size", "", db_batch_size}; const command_line::arg_descriptor<uint64_t> arg_pop_blocks = {"pop-blocks", "Remove blocks from end of blockchain", num_blocks}; @@ -680,7 +684,6 @@ int main(int argc, char* argv[]) if (! r) return 1; - log_level = command_line::get_arg(vm, arg_log_level); opt_verify = command_line::get_arg(vm, arg_verify); opt_batch = command_line::get_arg(vm, arg_batch); opt_resume = command_line::get_arg(vm, arg_resume); @@ -722,7 +725,12 @@ int main(int argc, char* argv[]) m_config_folder = command_line::get_arg(vm, data_dir_arg); db_arg_str = command_line::get_arg(vm, arg_database); - mlog_configure("monero-blockchain-import", true); + mlog_configure(mlog_get_default_log_path("monero-blockchain-import.log"), true); + if (!vm["log-level"].defaulted()) + mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); + else + mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str()); + MINFO("Starting..."); boost::filesystem::path fs_import_file_path; @@ -759,7 +767,11 @@ int main(int argc, char* argv[]) return 1; } - if ((db_type == "lmdb") || (db_type == "berkeley")) + if ((db_type == "lmdb") +#if defined(BERKELEY_DB) + || (db_type == "berkeley") +#endif + ) { db_engine_compiled = "blockchain_db"; } @@ -796,13 +808,11 @@ int main(int argc, char* argv[]) // properties to do so. Both ways work, but fake core isn't necessary in that // circumstance. - // for multi_db_runtime: - if (db_type == "lmdb" || db_type == "berkeley") - { - fake_core_db simple_core(m_config_folder, opt_testnet, opt_batch, db_type, db_flags); - import_from_file(simple_core, import_file_path, block_stop); - } - else + if (db_type != "lmdb" +#if defined(BERKELEY_DB) + && db_type != "berkeley" +#endif + ) { std::cerr << "database type unrecognized" << ENDL; return 1; @@ -847,4 +857,6 @@ int main(int argc, char* argv[]) // calls delete on its BlockchainDB derived class' object, which closes its // files. return 0; + + CATCH_ENTRY("Import error", 1); } diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h index 6ddda65ec..af934bf29 100644 --- a/src/blockchain_utilities/blockchain_utilities.h +++ b/src/blockchain_utilities/blockchain_utilities.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp index 7ccb9a145..63224225f 100644 --- a/src/blockchain_utilities/blocksdat_file.cpp +++ b/src/blockchain_utilities/blocksdat_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h index ed821cd6d..4e9d8173b 100644 --- a/src/blockchain_utilities/blocksdat_file.h +++ b/src/blockchain_utilities/blocksdat_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,8 +34,8 @@ #include <boost/iostreams/filtering_streambuf.hpp> -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_boost_serialization.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_core/blockchain.h" #include "blockchain_db/blockchain_db.h" #include "blockchain_db/lmdb/db_lmdb.h" diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index add2c65e6..d5bb37d93 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -458,7 +458,7 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path) bytes_read += chunk_size; // std::cout << refresh_string; - MINFO("Number bytes scanned: " << bytes_read); + MDEBUG("Number bytes scanned: " << bytes_read); } import_file.close(); diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index 0bbe28f17..1a646b54b 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,7 +34,7 @@ #include <boost/iostreams/filtering_streambuf.hpp> -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_core/blockchain.h" #include <algorithm> diff --git a/src/blockchain_utilities/bootstrap_serialization.h b/src/blockchain_utilities/bootstrap_serialization.h index a7d88907f..f9f50f00b 100644 --- a/src/blockchain_utilities/bootstrap_serialization.h +++ b/src/blockchain_utilities/bootstrap_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -28,8 +28,8 @@ #pragma once -#include "cryptonote_core/cryptonote_boost_serialization.h" -#include "cryptonote_core/difficulty.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" +#include "cryptonote_basic/difficulty.h" namespace cryptonote diff --git a/src/blockchain_utilities/cn_deserialize.cpp b/src/blockchain_utilities/cn_deserialize.cpp index ae8e38435..b178e4e03 100644 --- a/src/blockchain_utilities/cn_deserialize.cpp +++ b/src/blockchain_utilities/cn_deserialize.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -26,8 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/tx_extra.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/tx_extra.h" #include "cryptonote_core/blockchain.h" #include "blockchain_utilities.h" #include "common/command_line.h" diff --git a/src/blockchain_utilities/fake_core.h b/src/blockchain_utilities/fake_core.h index 814139602..70144184b 100644 --- a/src/blockchain_utilities/fake_core.h +++ b/src/blockchain_utilities/fake_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index f65503fbc..3a866af5b 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -30,8 +30,8 @@ if(APPLE) add_library(blocks STATIC blockexports.c) set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) else() - add_custom_command(OUTPUT blocks.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) - add_custom_command(OUTPUT testnet_blocks.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) + add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat) + add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat) add_library(blocks STATIC blocks.o testnet_blocks.o blockexports.c) set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C) endif() diff --git a/src/blocks/blockexports.c b/src/blocks/blockexports.c index 26adaad62..477167a12 100644 --- a/src/blocks/blockexports.c +++ b/src/blocks/blockexports.c @@ -2,12 +2,19 @@ #if defined(__APPLE__) #include <mach-o/getsect.h> - +#ifdef BUILD_SHARED_LIBS +#if !defined(__LP64__) +const struct mach_header _mh_execute_header; +#else +const struct mach_header_64 _mh_execute_header; +#endif +#else #if !defined(__LP64__) extern const struct mach_header _mh_execute_header; #else extern const struct mach_header_64 _mh_execute_header; #endif +#endif const unsigned char *get_blocks_dat_start(int testnet) { diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 1864c7835..0a3e8cd19 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -26,15 +26,20 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR}) + set(common_sources base58.cpp command_line.cpp dns_utils.cpp + download.cpp util.cpp i18n.cpp + password.cpp perf_timer.cpp task_region.cpp - thread_group.cpp) + thread_group.cpp + updates.cpp) if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) @@ -46,7 +51,9 @@ set(common_private_headers base58.h boost_serialization_helper.h command_line.h + common_fwd.h dns_utils.h + download.h http_connection.h int-util.h pod-class.h @@ -56,10 +63,12 @@ set(common_private_headers util.h varint.h i18n.h + password.h perf_timer.h stack_trace.h task_region.h - thread_group.h) + thread_group.h + updates.h) monero_private_headers(common ${common_private_headers}) @@ -69,6 +78,7 @@ monero_add_library(common ${common_private_headers}) target_link_libraries(common PUBLIC + epee crypto ${UNBOUND_LIBRARY} ${LIBUNWIND_LIBRARIES} @@ -78,6 +88,7 @@ target_link_libraries(common ${Boost_THREAD_LIBRARY} ${Boost_REGEX_LIBRARY} PRIVATE + ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) #monero_install_headers(common diff --git a/src/common/base58.cpp b/src/common/base58.cpp index 355d1e209..64cb7c0de 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/base58.h b/src/common/base58.h index df2ac2e9b..6dd850c03 100644 --- a/src/common/base58.h +++ b/src/common/base58.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h index 88dccbde7..4a503d830 100644 --- a/src/common/boost_serialization_helper.h +++ b/src/common/boost_serialization_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index d95859256..f71b3e576 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -76,7 +76,6 @@ namespace command_line const arg_descriptor<bool> arg_version = {"version", "Output version information"}; const arg_descriptor<std::string> arg_data_dir = {"data-dir", "Specify data directory"}; const arg_descriptor<std::string> arg_testnet_data_dir = {"testnet-data-dir", "Specify testnet data directory"}; - const arg_descriptor<std::string> arg_user_agent = {"user-agent", "Restrict RPC use to clients using this user agent"}; const arg_descriptor<bool> arg_test_drop_download = {"test-drop-download", "For net tests: in download, discard ALL blocks instead checking/saving them (very fast)"}; const arg_descriptor<uint64_t> arg_test_drop_download_height = {"test-drop-download-height", "Like test-drop-download but disards only after around certain height", 0}; const arg_descriptor<int> arg_test_dbg_lock_sleep = {"test-dbg-lock-sleep", "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests."}; @@ -121,4 +120,9 @@ namespace command_line , "How many blocks to sync at once during chain synchronization." , BLOCKS_SYNCHRONIZING_DEFAULT_COUNT }; + const command_line::arg_descriptor<std::string> arg_check_updates = { + "check-updates" + , "Check for new versions of monero: [disabled|notify|download|update]" + , "notify" + }; } diff --git a/src/common/command_line.h b/src/common/command_line.h index 3f0919e99..2110b8849 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -168,7 +168,7 @@ namespace command_line { return parser(); } - catch (std::exception& e) + catch (const std::exception& e) { std::cerr << "Failed to parse arguments: " << e.what() << std::endl; std::cerr << desc << std::endl; @@ -207,7 +207,6 @@ namespace command_line extern const arg_descriptor<bool> arg_version; extern const arg_descriptor<std::string> arg_data_dir; extern const arg_descriptor<std::string> arg_testnet_data_dir; - extern const arg_descriptor<std::string> arg_user_agent; extern const arg_descriptor<bool> arg_test_drop_download; extern const arg_descriptor<uint64_t> arg_test_drop_download_height; extern const arg_descriptor<int> arg_test_dbg_lock_sleep; @@ -219,4 +218,5 @@ namespace command_line extern const arg_descriptor<uint64_t> arg_prep_blocks_threads; extern const arg_descriptor<uint64_t> arg_show_time_stats; extern const arg_descriptor<size_t> arg_block_sync_size; + extern const arg_descriptor<std::string> arg_check_updates; } diff --git a/src/common/common_fwd.h b/src/common/common_fwd.h new file mode 100644 index 000000000..5d67251b1 --- /dev/null +++ b/src/common/common_fwd.h @@ -0,0 +1,41 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +namespace tools +{ + class DNSResolver; + struct login; + class password_container; + class t_http_connection; + class task_region; + class thread_group; +} diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 83259bc70..ab38cbbae 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ #include "common/command_line.h" #include "common/i18n.h" #include "common/dns_utils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include <cstring> #include <sstream> // check local first (in the event of static or in-source compilation of libunbound) @@ -37,6 +37,7 @@ #include <stdlib.h> #include "include_base_utils.h" +#include <random> #include <boost/filesystem/fstream.hpp> using namespace epee; namespace bf = boost::filesystem; @@ -404,21 +405,23 @@ std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec return addresses; } -std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid) +std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, bool cli_confirm) { // attempt to get address from dns query auto addresses = addresses_from_url(url, dnssec_valid); if (addresses.empty()) { - std::cout << tr("wrong address: ") << url; + LOG_ERROR("wrong address: " << url); return {}; } // for now, move on only if one address found if (addresses.size() > 1) { - std::cout << tr("not yet supported: Multiple Monero addresses found for given URL: ") << url; + LOG_ERROR("not yet supported: Multiple Monero addresses found for given URL: " << url); return {}; } + if (!cli_confirm) + return addresses[0]; // prompt user for confirmation. // inform user of DNSSEC validation status as well. std::string dnssec_str; @@ -451,6 +454,110 @@ std::string get_account_address_as_str_from_url(const std::string& url, bool& dn return addresses[0]; } +namespace +{ + bool dns_records_match(const std::vector<std::string>& a, const std::vector<std::string>& b) + { + if (a.size() != b.size()) return false; + + for (const auto& record_in_a : a) + { + bool ok = false; + for (const auto& record_in_b : b) + { + if (record_in_a == record_in_b) + { + ok = true; + break; + } + } + if (!ok) return false; + } + + return true; + } +} + +bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std::vector<std::string> &dns_urls) +{ + // Prevent infinite recursion when distributing + if (dns_urls.empty()) return false; + + std::vector<std::vector<std::string> > records; + records.resize(dns_urls.size()); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<int> dis(0, dns_urls.size() - 1); + size_t first_index = dis(gen); + + bool avail, valid; + size_t cur_index = first_index; + do + { + std::string url = dns_urls[cur_index]; + + records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid); + if (!avail) + { + records[cur_index].clear(); + LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping."); + } + if (!valid) + { + records[cur_index].clear(); + LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping."); + } + + cur_index++; + if (cur_index == dns_urls.size()) + { + cur_index = 0; + } + } while (cur_index != first_index); + + size_t num_valid_records = 0; + + for( const auto& record_set : records) + { + if (record_set.size() != 0) + { + num_valid_records++; + } + } + + if (num_valid_records < 2) + { + LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received"); + return false; + } + + int good_records_index = -1; + for (size_t i = 0; i < records.size() - 1; ++i) + { + if (records[i].size() == 0) continue; + + for (size_t j = i + 1; j < records.size(); ++j) + { + if (dns_records_match(records[i], records[j])) + { + good_records_index = i; + break; + } + } + if (good_records_index >= 0) break; + } + + if (good_records_index < 0) + { + LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched"); + return false; + } + + good_records = records[good_records_index]; + return true; +} + } // namespace tools::dns_utils } // namespace tools diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index 8a63a8129..53c0c1c7b 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ #include <vector> #include <string> -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" namespace tools { @@ -163,7 +163,9 @@ namespace dns_utils std::string address_from_txt_record(const std::string& s); std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec_valid); -std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid); +std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, bool cli_confirm = true); + +bool load_txt_records_from_dns(std::vector<std::string> &records, const std::vector<std::string> &dns_urls); } // namespace tools::dns_utils diff --git a/src/common/download.cpp b/src/common/download.cpp new file mode 100644 index 000000000..28aac5a59 --- /dev/null +++ b/src/common/download.cpp @@ -0,0 +1,270 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <string> +#include <atomic> +#include <boost/filesystem.hpp> +#include <boost/asio.hpp> +#include <boost/thread/thread.hpp> +#include "cryptonote_config.h" +#include "include_base_utils.h" +#include "net/http_client.h" +#include "download.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net.dl" + +namespace tools +{ + struct download_thread_control + { + const std::string path; + const std::string uri; + std::function<void(const std::string&, const std::string&, bool)> result_cb; + std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress_cb; + bool stop; + bool stopped; + bool success; + boost::thread thread; + boost::mutex mutex; + + download_thread_control(const std::string &path, const std::string &uri, std::function<void(const std::string&, const std::string&, bool)> result_cb, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress_cb): + path(path), uri(uri), result_cb(result_cb), progress_cb(progress_cb), stop(false), stopped(false), success(false) {} + ~download_thread_control() { if (thread.joinable()) thread.detach(); } + }; + + static void download_thread(download_async_handle control) + { + static std::atomic<unsigned int> thread_id(0); + + MLOG_SET_THREAD_NAME("DL" + std::to_string(thread_id++)); + + struct stopped_setter + { + stopped_setter(const download_async_handle &control): control(control) {} + ~stopped_setter() { control->stopped = true; } + download_async_handle control; + } stopped_setter(control); + + try + { + boost::unique_lock<boost::mutex> lock(control->mutex); + MINFO("Downloading " << control->uri << " to " << control->path); + std::ofstream f; + f.open(control->path, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + if (!f.good()) { + MERROR("Failed to open file " << control->path); + control->result_cb(control->path, control->uri, control->success); + return; + } + class download_client: public epee::net_utils::http::http_simple_client + { + public: + download_client(download_async_handle control, std::ofstream &f): + control(control), f(f), content_length(-1), total(0) {} + virtual ~download_client() { f.close(); } + virtual bool on_header(const epee::net_utils::http::http_response_info &headers) + { + ssize_t length; + if (epee::string_tools::get_xtype_from_string(length, headers.m_header_info.m_content_length) && length >= 0) + { + MINFO("Content-Length: " << length); + content_length = length; + boost::filesystem::path path(control->path); + boost::filesystem::space_info si = boost::filesystem::space(path); + if (si.available < (size_t)content_length) + { + const uint64_t avail = (si.available + 1023) / 1024, needed = (content_length + 1023) / 1024; + MERROR("Not enough space to download " << needed << " kB to " << path << " (" << avail << " kB available)"); + return false; + } + } + return true; + } + virtual bool handle_target_data(std::string &piece_of_transfer) + { + try + { + boost::lock_guard<boost::mutex> lock(control->mutex); + if (control->stop) + return false; + f << piece_of_transfer; + total += piece_of_transfer.size(); + if (control->progress_cb && !control->progress_cb(control->path, control->uri, total, content_length)) + return false; + return f.good(); + } + catch (const std::exception &e) + { + MERROR("Error writing data: " << e.what()); + return false; + } + } + private: + download_async_handle control; + std::ofstream &f; + ssize_t content_length; + size_t total; + } client(control, f); + epee::net_utils::http::url_content u_c; + if (!epee::net_utils::parse_url(control->uri, u_c)) + { + MERROR("Failed to parse URL " << control->uri); + control->result_cb(control->path, control->uri, control->success); + return; + } + if (u_c.host.empty()) + { + MERROR("Failed to determine address from URL " << control->uri); + control->result_cb(control->path, control->uri, control->success); + return; + } + + lock.unlock(); + + uint16_t port = u_c.port ? u_c.port : 80; + MDEBUG("Connecting to " << u_c.host << ":" << port); + client.set_server(u_c.host, std::to_string(port), boost::none); + if (!client.connect(std::chrono::seconds(30))) + { + boost::lock_guard<boost::mutex> lock(control->mutex); + MERROR("Failed to connect to " << control->uri); + control->result_cb(control->path, control->uri, control->success); + return; + } + MDEBUG("GETting " << u_c.uri); + const epee::net_utils::http::http_response_info *info = NULL; + if (!client.invoke_get(u_c.uri, std::chrono::seconds(30), "", &info)) + { + boost::lock_guard<boost::mutex> lock(control->mutex); + MERROR("Failed to connect to " << control->uri); + client.disconnect(); + control->result_cb(control->path, control->uri, control->success); + return; + } + if (control->stop) + { + boost::lock_guard<boost::mutex> lock(control->mutex); + MDEBUG("Download cancelled"); + client.disconnect(); + control->result_cb(control->path, control->uri, control->success); + return; + } + if (!info) + { + boost::lock_guard<boost::mutex> lock(control->mutex); + MERROR("Failed invoking GET command to " << control->uri << ", no status info returned"); + client.disconnect(); + control->result_cb(control->path, control->uri, control->success); + return; + } + MDEBUG("response code: " << info->m_response_code); + MDEBUG("response length: " << info->m_header_info.m_content_length); + MDEBUG("response comment: " << info->m_response_comment); + MDEBUG("response body: " << info->m_body); + for (const auto &f: info->m_additional_fields) + MDEBUG("additional field: " << f.first << ": " << f.second); + if (info->m_response_code != 200) + { + boost::lock_guard<boost::mutex> lock(control->mutex); + MERROR("Status code " << info->m_response_code); + client.disconnect(); + control->result_cb(control->path, control->uri, control->success); + return; + } + client.disconnect(); + f.close(); + MDEBUG("Download complete"); + lock.lock(); + control->success = true; + control->result_cb(control->path, control->uri, control->success); + return; + } + catch (const std::exception &e) + { + MERROR("Exception in download thread: " << e.what()); + // fall through and call result_cb not from the catch block to avoid another exception + } + boost::lock_guard<boost::mutex> lock(control->mutex); + control->result_cb(control->path, control->uri, control->success); + } + + bool download(const std::string &path, const std::string &url, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> cb) + { + bool success = false; + download_async_handle handle = download_async(path, url, [&success](const std::string&, const std::string&, bool result) {success = result;}, cb); + download_wait(handle); + return success; + } + + download_async_handle download_async(const std::string &path, const std::string &url, std::function<void(const std::string&, const std::string&, bool)> result, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress) + { + download_async_handle control = std::make_shared<download_thread_control>(path, url, result, progress); + control->thread = boost::thread([control](){ download_thread(control); }); + return control; + } + + bool download_finished(const download_async_handle &control) + { + CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle"); + boost::lock_guard<boost::mutex> lock(control->mutex); + return control->stopped; + } + + bool download_error(const download_async_handle &control) + { + CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle"); + boost::lock_guard<boost::mutex> lock(control->mutex); + return !control->success; + } + + bool download_wait(const download_async_handle &control) + { + CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle"); + { + boost::lock_guard<boost::mutex> lock(control->mutex); + if (control->stopped) + return true; + } + control->thread.join(); + return true; + } + + bool download_cancel(const download_async_handle &control) + { + CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle"); + { + boost::lock_guard<boost::mutex> lock(control->mutex); + if (control->stopped) + return true; + control->stop = true; + } + control->thread.join(); + return true; + } +} diff --git a/src/common/download.h b/src/common/download.h new file mode 100644 index 000000000..917cb2278 --- /dev/null +++ b/src/common/download.h @@ -0,0 +1,44 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <string> + +namespace tools +{ + struct download_thread_control; + typedef std::shared_ptr<download_thread_control> download_async_handle; + + bool download(const std::string &path, const std::string &url, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress = NULL); + download_async_handle download_async(const std::string &path, const std::string &url, std::function<void(const std::string&, const std::string&, bool)> result, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress = NULL); + bool download_error(const download_async_handle &h); + bool download_finished(const download_async_handle &h); + bool download_wait(const download_async_handle &h); + bool download_cancel(const download_async_handle &h); +} diff --git a/src/common/http_connection.h b/src/common/http_connection.h index 8a786361a..0357a90a0 100644 --- a/src/common/http_connection.h +++ b/src/common/http_connection.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp index 05eea3b66..4a76e76fc 100644 --- a/src/common/i18n.cpp +++ b/src/common/i18n.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/i18n.h b/src/common/i18n.h index 332602185..5169cf9f7 100644 --- a/src/common/i18n.h +++ b/src/common/i18n.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/int-util.h b/src/common/int-util.h index 9ac20e582..34288805a 100644 --- a/src/common/int-util.h +++ b/src/common/int-util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/password_container.cpp b/src/common/password.cpp index 832b93a1a..bdc9c69c0 100644 --- a/src/wallet/password_container.cpp +++ b/src/common/password.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -28,7 +28,7 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#include "password_container.h" +#include "password.h" #include <iostream> #include <memory.h> @@ -245,4 +245,27 @@ namespace tools return boost::none; } + + boost::optional<login> login::parse(std::string&& userpass, bool verify, const char* message) + { + login out{}; + password_container wipe{std::move(userpass)}; + + const auto loc = wipe.password().find(':'); + if (loc == std::string::npos) + { + auto result = tools::password_container::prompt(verify, message); + if (!result) + return boost::none; + + out.password = std::move(*result); + } + else + { + out.password = password_container{wipe.password().substr(loc + 1)}; + } + + out.username = wipe.password().substr(0, loc); + return {std::move(out)}; + } } diff --git a/src/wallet/password_container.h b/src/common/password.h index 9c6faf9c8..12f715df4 100644 --- a/src/wallet/password_container.h +++ b/src/common/password.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -64,4 +64,33 @@ namespace tools //! TODO Custom allocator that locks to RAM? std::string m_password; }; + + struct login + { + login() = default; + + /*! + Extracts username and password from the format `username:password`. A + blank username or password is allowed. If the `:` character is not + present, `password_container::prompt` will be called by forwarding the + `verify` and `message` arguments. + + \param userpass Is "consumed", and the memory contents are wiped. + \param verify is passed to `password_container::prompt` if necessary. + \param message is passed to `password_container::prompt` if necessary. + + \return The username and password, or boost::none if + `password_container::prompt` fails. + */ + static boost::optional<login> parse(std::string&& userpass, bool verify, const char* message = "Password"); + + login(const login&) = delete; + login(login&&) = default; + ~login() = default; + login& operator=(const login&) = delete; + login& operator=(login&&) = default; + + std::string username; + password_container password; + }; } diff --git a/src/common/pod-class.h b/src/common/pod-class.h index f3f241552..3896d5c29 100644 --- a/src/common/pod-class.h +++ b/src/common/pod-class.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h index f5ecc8b50..8494b4a60 100644 --- a/src/common/rpc_client.h +++ b/src/common/rpc_client.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -28,10 +28,13 @@ #pragma once +#include <boost/optional/optional.hpp> + #include "common/http_connection.h" #include "common/scoped_message_writer.h" #include "rpc/core_rpc_server_commands_defs.h" #include "storages/http_abstract_invoke.h" +#include "net/http_auth.h" #include "net/http_client.h" #include "string_tools.h" @@ -45,11 +48,12 @@ namespace tools t_rpc_client( uint32_t ip , uint16_t port + , boost::optional<epee::net_utils::http::login> user ) : m_http_client{} { m_http_client.set_server( - epee::string_tools::get_ip_string_from_int32(ip), std::to_string(port) + epee::string_tools::get_ip_string_from_int32(ip), std::to_string(port), std::move(user) ); } diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h index f82181926..7ee4f1379 100644 --- a/src/common/scoped_message_writer.h +++ b/src/common/scoped_message_writer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp index 67065aae7..ef64c20c5 100644 --- a/src/common/stack_trace.cpp +++ b/src/common/stack_trace.cpp @@ -26,7 +26,7 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ +#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__ #define USE_UNWIND #endif diff --git a/src/common/task_region.cpp b/src/common/task_region.cpp index b53a8376a..9b4620c6e 100644 --- a/src/common/task_region.cpp +++ b/src/common/task_region.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/task_region.h b/src/common/task_region.h index e4d210661..30972cce3 100644 --- a/src/common/task_region.h +++ b/src/common/task_region.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/thread_group.cpp b/src/common/thread_group.cpp index 4e1cc8964..860d0b732 100644 --- a/src/common/thread_group.cpp +++ b/src/common/thread_group.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/thread_group.h b/src/common/thread_group.h index 10add89e0..48fd4cd56 100644 --- a/src/common/thread_group.h +++ b/src/common/thread_group.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h index b2d5b27a6..4d82a1364 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/common/unordered_containers_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/common/updates.cpp b/src/common/updates.cpp new file mode 100644 index 000000000..5b1acf5fa --- /dev/null +++ b/src/common/updates.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "util.h" +#include "dns_utils.h" +#include "updates.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "updates" + +namespace tools +{ + bool check_updates(const std::string &software, const std::string &buildtag, std::string &version, std::string &hash) + { + std::vector<std::string> records; + bool found = false; + + MDEBUG("Checking updates for " << buildtag << " " << software); + + // All four MoneroPulse domains have DNSSEC on and valid + static const std::vector<std::string> dns_urls = { + "updates.moneropulse.org", + "updates.moneropulse.net", + "updates.moneropulse.co", + "updates.moneropulse.se" + }; + + if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls)) + return false; + + for (const auto& record : records) + { + std::vector<std::string> fields; + boost::split(fields, record, boost::is_any_of(":")); + if (fields.size() != 4) + { + MWARNING("Updates record does not have 4 fields: " << record); + continue; + } + + if (software != fields[0] || buildtag != fields[1]) + continue; + + bool alnum = true; + for (auto c: hash) + if (!isalnum(c)) + alnum = false; + if (hash.size() != 64 && !alnum) + { + MWARNING("Invalid hash: " << hash); + continue; + } + + // use highest version + if (found) + { + int cmp = vercmp(version.c_str(), fields[2].c_str()); + if (cmp > 0) + continue; + if (cmp == 0 && hash != fields[3]) + MWARNING("Two matches found for " << software << " version " << version << " on " << buildtag); + } + + version = fields[2]; + hash = fields[3]; + + MINFO("Found new version " << version << " with hash " << hash); + found = true; + } + return found; + } + + std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version, bool user) + { + const char *base = user ? "https://downloads.getmonero.org/" : "http://updates.getmonero.org/"; +#ifdef _WIN32 + static const char extension[] = ".zip"; +#else + static const char extension[] = ".tar.bz2"; +#endif + + std::string url; + + url = base; + if (!subdir.empty()) + url += subdir + "/"; + url = url + software + "-" + buildtag + "-v" + version + extension; + return url; + } +} diff --git a/src/common/updates.h b/src/common/updates.h new file mode 100644 index 000000000..e494ed7ac --- /dev/null +++ b/src/common/updates.h @@ -0,0 +1,37 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <string> + +namespace tools +{ + bool check_updates(const std::string &software, const std::string &buildtag, std::string &version, std::string &hash); + std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version, bool user); +} diff --git a/src/common/util.cpp b/src/common/util.cpp index bfcf86bc6..046961b06 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,6 +31,7 @@ #include <cstdio> #include "include_base_utils.h" +#include "file_io_utils.h" using namespace epee; #include "util.h" @@ -46,7 +47,7 @@ using namespace epee; #endif #include <boost/filesystem.hpp> #include <boost/asio.hpp> - +#include <openssl/sha.h> namespace tools { @@ -568,4 +569,65 @@ std::string get_nix_version_display_string() MDEBUG("Address '" << address << "' is not local"); return false; } + int vercmp(const char *v0, const char *v1) + { + std::vector<std::string> f0, f1; + boost::split(f0, v0, boost::is_any_of(".")); + boost::split(f1, v1, boost::is_any_of(".")); + while (f0.size() < f1.size()) + f0.push_back("0"); + while (f1.size() < f0.size()) + f1.push_back("0"); + for (size_t i = 0; i < f0.size(); ++i) { + int f0i = atoi(f0[i].c_str()), f1i = atoi(f1[i].c_str()); + int n = f0i - f1i; + if (n) + return n; + } + return 0; + } + + bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash) + { + SHA256_CTX ctx; + if (!SHA256_Init(&ctx)) + return false; + if (!SHA256_Update(&ctx, data, len)) + return false; + if (!SHA256_Final((unsigned char*)hash.data, &ctx)) + return false; + return true; + } + + bool sha256sum(const std::string &filename, crypto::hash &hash) + { + if (!epee::file_io_utils::is_file_exist(filename)) + return false; + std::ifstream f; + f.exceptions(std::ifstream::failbit | std::ifstream::badbit); + f.open(filename, std::ios_base::binary | std::ios_base::in | std::ios::ate); + if (!f) + return false; + std::ifstream::pos_type file_size = f.tellg(); + SHA256_CTX ctx; + if (!SHA256_Init(&ctx)) + return false; + size_t size_left = file_size; + f.seekg(0, std::ios::beg); + while (size_left) + { + char buf[4096]; + std::ifstream::pos_type read_size = size_left > sizeof(buf) ? sizeof(buf) : size_left; + f.read(buf, read_size); + if (!f || !f.good()) + return false; + if (!SHA256_Update(&ctx, buf, read_size)) + return false; + size_left -= read_size; + } + f.close(); + if (!SHA256_Final((unsigned char*)hash.data, &ctx)) + return false; + return true; + } } diff --git a/src/common/util.h b/src/common/util.h index c2ffc44ca..4291d7e18 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -183,4 +183,8 @@ namespace tools unsigned get_max_concurrency(); bool is_local_address(const std::string &address); + int vercmp(const char *v0, const char *v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate + + bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash); + bool sha256sum(const std::string &filename, crypto::hash &hash); } diff --git a/src/common/varint.h b/src/common/varint.h index ffaa682c5..cb785e61a 100644 --- a/src/common/varint.h +++ b/src/common/varint.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 1e037a07d..277ee64c2 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -93,7 +93,7 @@ endif() # Because of the way Qt works on android with JNI, the code does not live in the main android thread # So this code runs with a 1 MB default stack size. # This will force the use of the heap for the allocation of the scratchpad -if (ANDROID) +if (ANDROID OR IOS) if( BUILD_GUI_DEPS ) add_definitions(-DFORCE_USE_HEAP=1) endif() diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c index 1cb1cf344..1e43f9c4d 100644 --- a/src/crypto/blake256.c +++ b/src/crypto/blake256.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/blake256.h b/src/crypto/blake256.h index e262d1b4b..921fcd2fd 100644 --- a/src/crypto/blake256.h +++ b/src/crypto/blake256.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h index 94c0ba721..80557e9f5 100644 --- a/src/crypto/chacha8.h +++ b/src/crypto/chacha8.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index 60ee38096..4bd75b77c 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 1b390e402..4edfee0ce 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 4986499f4..37edf5b6d 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 6ceb944cd..98da466cc 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 9ca835d9e..3b8c7996b 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/README.md b/src/crypto/crypto_ops_builder/README.md index eec3e21e7..3b87966f5 100644 --- a/src/crypto/crypto_ops_builder/README.md +++ b/src/crypto/crypto_ops_builder/README.md @@ -1,6 +1,6 @@ # Monero -Copyright (c) 2014-2016, The Monero Project +Copyright (c) 2014-2017, The Monero Project ## Crypto Ops Builder diff --git a/src/crypto/crypto_ops_builder/crypto-ops-data.c b/src/crypto/crypto_ops_builder/crypto-ops-data.c index 60ee38096..4bd75b77c 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops-data.c +++ b/src/crypto/crypto_ops_builder/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/crypto-ops-old.c b/src/crypto/crypto_ops_builder/crypto-ops-old.c index 910801c57..b7a290b4a 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops-old.c +++ b/src/crypto/crypto_ops_builder/crypto-ops-old.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/crypto-ops.h b/src/crypto/crypto_ops_builder/crypto-ops.h index 84ef12ae2..47d5b46ae 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops.h +++ b/src/crypto/crypto_ops_builder/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py index 9d996f9d1..5f8776a49 100644 --- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py +++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py @@ -15,7 +15,7 @@ print("maybe someone smart can replace the sed with perl..") a = "" license = textwrap.dedent("""\ - // Copyright (c) 2014-2016, The Monero Project + // Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h index cdc5ac1ee..b432efade 100644 --- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h +++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index a8d8fdb19..1a135ffcf 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/groestl.h b/src/crypto/groestl.h index ac749d1d8..89a073a4c 100644 --- a/src/crypto/groestl.h +++ b/src/crypto/groestl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h index c271fd367..8fa6d7a83 100644 --- a/src/crypto/groestl_tables.h +++ b/src/crypto/groestl_tables.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c index 15357dc7a..236479880 100644 --- a/src/crypto/hash-extra-blake.c +++ b/src/crypto/hash-extra-blake.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c index 69546ee44..b15075306 100644 --- a/src/crypto/hash-extra-groestl.c +++ b/src/crypto/hash-extra-groestl.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c index 5da0894f6..8950687d3 100644 --- a/src/crypto/hash-extra-jh.c +++ b/src/crypto/hash-extra-jh.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c index babf5006f..e63e7da20 100644 --- a/src/crypto/hash-extra-skein.c +++ b/src/crypto/hash-extra-skein.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 612d94efe..6e3a5c6c9 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash.c b/src/crypto/hash.c index 93f7353e4..ed95391d8 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 803992078..22991e513 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index fefc3b7c3..619038ae6 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/random.c b/src/crypto/random.c index 6a9f63c12..691c31f62 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/random.h b/src/crypto/random.h index b0d2303b6..75d23fd04 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h index 0b3d071ee..a06ef30a2 100644 --- a/src/crypto/skein_port.h +++ b/src/crypto/skein_port.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 43b9619f3..6afa28934 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -44,6 +44,9 @@ #define INIT_SIZE_BLK 8 #define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) +extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); +extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); + #if defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64)) // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI // Fall back to more portable code is down at the bottom @@ -138,9 +141,6 @@ #define THREADV __thread #endif -extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); -extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); - #pragma pack(push, 1) union cn_slow_hash_state { @@ -494,7 +494,7 @@ void slow_hash_free_state(void) * buffer of pseudorandom data by hashing the supplied data. It then uses this * random data to fill a large 2MB buffer with pseudorandom data by iteratively * encrypting it using 10 rounds of AES per entry. After this initialization, - * it executes 500,000 rounds of mixing through the random 2MB buffer using + * it executes 524,288 rounds of mixing through the random 2MB buffer using * AES (typically provided in hardware on modern CPUs) and a 64 bit multiply. * Finally, it re-mixes this large buffer back into * the 200 byte "text" buffer, and then hashes this buffer using one of four @@ -530,7 +530,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) size_t i, j; uint64_t *p = NULL; - oaes_ctx *aes_ctx; + oaes_ctx *aes_ctx = NULL; int useAes = !force_software_aes() && check_aes_hw(); static void (*const extra_hashes[4])(const void *, size_t, char *) = @@ -578,8 +578,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash) U64(b)[0] = U64(&state.k[16])[0] ^ U64(&state.k[48])[0]; U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1]; - /* CryptoNight Step 3: Bounce randomly 1 million times through the mixing buffer, - * using 500,000 iterations of the following mixing function. Each execution + /* CryptoNight Step 3: Bounce randomly 1,048,576 times (1<<20) through the mixing buffer, + * using 524,288 iterations of the following mixing function. Each execution * performs two reads and writes from the mixing buffer. */ @@ -722,32 +722,24 @@ union cn_slow_hash_state * key schedule. Don't try to use this for vanilla AES. */ static void aes_expand_key(const uint8_t *key, uint8_t *expandedKey) { -__asm__("mov x2, %1\n\t" : : "r"(key), "r"(expandedKey)); +static const int rcon[] = { + 0x01,0x01,0x01,0x01, + 0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d, // rotate-n-splat + 0x1b,0x1b,0x1b,0x1b }; __asm__( -" adr x3,Lrcon\n" -"\n" " eor v0.16b,v0.16b,v0.16b\n" -" ld1 {v3.16b},[x0],#16\n" -" ld1 {v1.4s,v2.4s},[x3],#32\n" -" b L256\n" -".align 5\n" -"Lrcon:\n" -".long 0x01,0x01,0x01,0x01\n" -".long 0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d // rotate-n-splat\n" -".long 0x1b,0x1b,0x1b,0x1b\n" -"\n" -".align 4\n" -"L256:\n" -" ld1 {v4.16b},[x0]\n" -" mov w1,#5\n" -" st1 {v3.4s},[x2],#16\n" +" ld1 {v3.16b},[%0],#16\n" +" ld1 {v1.4s,v2.4s},[%2],#32\n" +" ld1 {v4.16b},[%0]\n" +" mov w2,#5\n" +" st1 {v3.4s},[%1],#16\n" "\n" -"Loop256:\n" +"1:\n" " tbl v6.16b,{v4.16b},v2.16b\n" " ext v5.16b,v0.16b,v3.16b,#12\n" -" st1 {v4.4s},[x2],#16\n" +" st1 {v4.4s},[%1],#16\n" " aese v6.16b,v0.16b\n" -" subs w1,w1,#1\n" +" subs w2,w2,#1\n" "\n" " eor v3.16b,v3.16b,v5.16b\n" " ext v5.16b,v0.16b,v5.16b,#12\n" @@ -757,8 +749,8 @@ __asm__( " eor v3.16b,v3.16b,v5.16b\n" " shl v1.16b,v1.16b,#1\n" " eor v3.16b,v3.16b,v6.16b\n" -" st1 {v3.4s},[x2],#16\n" -" b.eq Ldone\n" +" st1 {v3.4s},[%1],#16\n" +" b.eq 2f\n" "\n" " dup v6.4s,v3.s[3] // just splat\n" " ext v5.16b,v0.16b,v4.16b,#12\n" @@ -771,9 +763,9 @@ __asm__( " eor v4.16b,v4.16b,v5.16b\n" "\n" " eor v4.16b,v4.16b,v6.16b\n" -" b Loop256\n" +" b 1b\n" "\n" -"Ldone:\n"); +"2:\n" : : "r"(key), "r"(expandedKey), "r"(rcon)); } /* An ordinary AES round is a sequence of SubBytes, ShiftRows, MixColumns, AddRoundKey. There @@ -895,8 +887,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash) U64(b)[0] = U64(&state.k[16])[0] ^ U64(&state.k[48])[0]; U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1]; - /* CryptoNight Step 3: Bounce randomly 1 million times through the mixing buffer, - * using 500,000 iterations of the following mixing function. Each execution + /* CryptoNight Step 3: Bounce randomly 1,048,576 times (1<<20) through the mixing buffer, + * using 524,288 iterations of the following mixing function. Each execution * performs two reads and writes from the mixing buffer. */ diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 5cdaa8c94..eb98c31b7 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt new file mode 100644 index 000000000..2b8ad365a --- /dev/null +++ b/src/cryptonote_basic/CMakeLists.txt @@ -0,0 +1,73 @@ +# Copyright (c) 2014-2017, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(cryptonote_basic_sources + account.cpp + checkpoints.cpp + cryptonote_basic_impl.cpp + cryptonote_format_utils.cpp + difficulty.cpp + hardfork.cpp + miner.cpp) + +set(cryptonote_basic_headers) + +set(cryptonote_basic_private_headers + account.h + account_boost_serialization.h + checkpoints.h + connection_context.h + cryptonote_basic.h + cryptonote_basic_impl.h + cryptonote_boost_serialization.h + cryptonote_format_utils.h + cryptonote_stat_info.h + difficulty.h + hardfork.h + miner.h + tx_extra.h + verification_context.h) + +monero_private_headers(cryptonote_basic + ${crypto_private_headers}) +monero_add_library(cryptonote_basic + ${cryptonote_basic_sources} + ${cryptonote_basic_headers} + ${cryptonote_basic_private_headers}) +target_link_libraries(cryptonote_basic + PUBLIC + common + crypto + ${Boost_DATE_TIME_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SERIALIZATION_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + PRIVATE + ${EXTRA_LIBRARIES}) diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_basic/account.cpp index 8f2db6863..dd875402f 100644 --- a/src/cryptonote_core/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -38,8 +38,8 @@ extern "C" { #include "crypto/keccak.h" } -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic_impl.h" +#include "cryptonote_format_utils.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "account" diff --git a/src/cryptonote_core/account.h b/src/cryptonote_basic/account.h index 41a119b07..e0d5447a2 100644 --- a/src/cryptonote_core/account.h +++ b/src/cryptonote_basic/account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,7 +30,7 @@ #pragma once -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic.h" #include "crypto/crypto.h" #include "serialization/keyvalue_serialization.h" diff --git a/src/cryptonote_core/account_boost_serialization.h b/src/cryptonote_basic/account_boost_serialization.h index 4151d9b50..d2f541638 100644 --- a/src/cryptonote_core/account_boost_serialization.h +++ b/src/cryptonote_basic/account_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,7 +31,7 @@ #pragma once #include "account.h" -#include "cryptonote_core/cryptonote_boost_serialization.h" +#include "cryptonote_boost_serialization.h" //namespace cryptonote { namespace boost diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_basic/checkpoints.cpp index 3cf804ede..103a4a33e 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/cryptonote_basic/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -42,30 +42,6 @@ using namespace epee; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "checkpoints" -namespace -{ - bool dns_records_match(const std::vector<std::string>& a, const std::vector<std::string>& b) - { - if (a.size() != b.size()) return false; - - for (const auto& record_in_a : a) - { - bool ok = false; - for (const auto& record_in_b : b) - { - if (record_in_a == record_in_b) - { - ok = true; - break; - } - } - if (!ok) return false; - } - - return true; - } -} // anonymous namespace - namespace cryptonote { //--------------------------------------------------------------------------- @@ -230,6 +206,8 @@ namespace cryptonote bool checkpoints::load_checkpoints_from_dns(bool testnet) { + std::vector<std::string> records; + // All four MoneroPulse domains have DNSSEC on and valid static const std::vector<std::string> dns_urls = { "checkpoints.moneropulse.se" , "checkpoints.moneropulse.org" @@ -243,87 +221,10 @@ namespace cryptonote , "testpoints.moneropulse.co" }; - std::vector<std::vector<std::string> > records; - records.resize(dns_urls.size()); - - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<int> dis(0, dns_urls.size() - 1); - size_t first_index = dis(gen); - - bool avail, valid; - size_t cur_index = first_index; - do - { - std::string url; - if (testnet) - { - url = testnet_dns_urls[cur_index]; - } - else - { - url = dns_urls[cur_index]; - } - - records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid); - if (!avail) - { - records[cur_index].clear(); - LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping."); - } - if (!valid) - { - records[cur_index].clear(); - LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping."); - } - - cur_index++; - if (cur_index == dns_urls.size()) - { - cur_index = 0; - } - records[cur_index].clear(); - } while (cur_index != first_index); - - size_t num_valid_records = 0; - - for( const auto& record_set : records) - { - if (record_set.size() != 0) - { - num_valid_records++; - } - } - - if (num_valid_records < 2) - { - LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received"); - return true; - } - - int good_records_index = -1; - for (size_t i = 0; i < records.size() - 1; ++i) - { - if (records[i].size() == 0) continue; - - for (size_t j = i + 1; j < records.size(); ++j) - { - if (dns_records_match(records[i], records[j])) - { - good_records_index = i; - break; - } - } - if (good_records_index >= 0) break; - } - - if (good_records_index < 0) - { - LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched"); - return true; - } + if (!tools::dns_utils::load_txt_records_from_dns(records, testnet ? testnet_dns_urls : dns_urls)) + return true; // why true ? - for (auto& record : records[good_records_index]) + for (const auto& record : records) { auto pos = record.find(":"); if (pos != std::string::npos) diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_basic/checkpoints.h index 71727753e..3a74d8a69 100644 --- a/src/cryptonote_core/checkpoints.h +++ b/src/cryptonote_basic/checkpoints.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/connection_context.h b/src/cryptonote_basic/connection_context.h index 7e62e77b9..8d739900e 100644 --- a/src/cryptonote_core/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index da069a21a..c4adf1fcb 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -35,6 +35,7 @@ #include <vector> #include <cstring> // memcmp #include <sstream> +#include <atomic> #include "serialization/serialization.h" #include "serialization/variant.h" #include "serialization/vector.h" @@ -43,7 +44,6 @@ #include "serialization/debug_archive.h" #include "serialization/crypto.h" #include "serialization/keyvalue_serialization.h" // eepe named serialization -#include "string_tools.h" #include "cryptonote_config.h" #include "crypto/crypto.h" #include "crypto/hash.h" @@ -186,15 +186,37 @@ namespace cryptonote class transaction: public transaction_prefix { + private: + // hash cash + mutable std::atomic<bool> hash_valid; + mutable std::atomic<bool> blob_size_valid; + public: std::vector<std::vector<crypto::signature> > signatures; //count signatures always the same as inputs count rct::rctSig rct_signatures; + // hash cash + mutable crypto::hash hash; + mutable size_t blob_size; + transaction(); + transaction(const transaction &t): transaction_prefix(t), hash_valid(false), blob_size_valid(false), signatures(t.signatures), rct_signatures(t.rct_signatures) { if (t.is_hash_valid()) { hash = t.hash; set_hash_valid(true); } if (t.is_blob_size_valid()) { blob_size = t.blob_size; set_blob_size_valid(true); } } + transaction &operator=(const transaction &t) { transaction_prefix::operator=(t); set_hash_valid(false); set_blob_size_valid(false); signatures = t.signatures; rct_signatures = t.rct_signatures; if (t.is_hash_valid()) { hash = t.hash; set_hash_valid(true); } if (t.is_blob_size_valid()) { blob_size = t.blob_size; set_blob_size_valid(true); } return *this; } virtual ~transaction(); void set_null(); + void invalidate_hashes(); + bool is_hash_valid() const { return hash_valid.load(std::memory_order_acquire); } + void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); } + bool is_blob_size_valid() const { return blob_size_valid.load(std::memory_order_acquire); } + void set_blob_size_valid(bool v) const { blob_size_valid.store(v,std::memory_order_release); } BEGIN_SERIALIZE_OBJECT() + if (!typename Archive<W>::is_saving()) + { + set_hash_valid(false); + set_blob_size_valid(false); + } + FIELDS(*static_cast<transaction_prefix *>(this)) if (version == 1) @@ -250,6 +272,28 @@ namespace cryptonote } END_SERIALIZE() + template<bool W, template <bool> class Archive> + bool serialize_base(Archive<W> &ar) + { + FIELDS(*static_cast<transaction_prefix *>(this)) + + if (version == 1) + { + } + else + { + ar.tag("rct_signatures"); + if (!vin.empty()) + { + ar.begin_object(); + bool r = rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size()); + if (!r || !ar.stream().good()) return false; + ar.end_object(); + } + } + return true; + } + private: static size_t get_signature_size(const txin_v& tx_in); }; @@ -277,6 +321,15 @@ namespace cryptonote extra.clear(); signatures.clear(); rct_signatures.type = rct::RCTTypeNull; + set_hash_valid(false); + set_blob_size_valid(false); + } + + inline + void transaction::invalidate_hashes() + { + set_hash_valid(false); + set_blob_size_valid(false); } inline @@ -317,10 +370,28 @@ namespace cryptonote struct block: public block_header { + private: + // hash cash + mutable std::atomic<bool> hash_valid; + + public: + block(): block_header(), hash_valid(false) {} + block(const block &b): block_header(b), hash_valid(false), miner_tx(b.miner_tx), tx_hashes(b.tx_hashes) { if (b.is_hash_valid()) { hash = b.hash; set_hash_valid(true); } } + block &operator=(const block &b) { block_header::operator=(b); hash_valid = false; miner_tx = b.miner_tx; tx_hashes = b.tx_hashes; if (b.is_hash_valid()) { hash = b.hash; set_hash_valid(true); } return *this; } + void invalidate_hashes() { set_hash_valid(false); } + bool is_hash_valid() const { return hash_valid.load(std::memory_order_acquire); } + void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); } + transaction miner_tx; std::vector<crypto::hash> tx_hashes; + // hash cash + mutable crypto::hash hash; + BEGIN_SERIALIZE_OBJECT() + if (!typename Archive<W>::is_saving()) + set_hash_valid(false); + FIELDS(*static_cast<block_header *>(this)) FIELD(miner_tx) FIELD(tx_hashes) diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index 338210f01..edd67793f 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -67,6 +67,15 @@ namespace cryptonote { /* Cryptonote helper functions */ /************************************************************************/ //----------------------------------------------------------------------------------------------- + size_t get_min_block_size(uint8_t version) + { + if (version < 2) + return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + if (version < 5) + return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; + } + //----------------------------------------------------------------------------------------------- size_t get_max_block_size() { return CRYPTONOTE_MAX_BLOCK_SIZE; @@ -89,7 +98,7 @@ namespace cryptonote { base_reward = FINAL_SUBSIDY_PER_MINUTE*target_minutes; } - uint64_t full_reward_zone = version < 2 ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + uint64_t full_reward_zone = get_min_block_size(version); //make it soft if (median_size < full_reward_zone) { @@ -299,12 +308,13 @@ namespace cryptonote { , crypto::hash8& payment_id , bool testnet , const std::string& str_or_url + , bool cli_confirm ) { if (get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, str_or_url)) return true; bool dnssec_valid; - std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid); + std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid, cli_confirm); return !address_str.empty() && get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_str); } @@ -313,11 +323,12 @@ namespace cryptonote { cryptonote::account_public_address& address , bool testnet , const std::string& str_or_url + , bool cli_confirm ) { bool has_payment_id; crypto::hash8 payment_id; - return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url); + return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url, cli_confirm); } //-------------------------------------------------------------------------------- bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 5703a7d75..14c03ac4c 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,6 +33,8 @@ #include "cryptonote_basic.h" #include "crypto/crypto.h" #include "crypto/hash.h" +#include "hex.h" +#include "span.h" namespace cryptonote { @@ -69,6 +71,7 @@ namespace cryptonote { /************************************************************************/ /* Cryptonote helper functions */ /************************************************************************/ + size_t get_min_block_size(uint8_t version); size_t get_max_block_size(); size_t get_max_tx_size(); bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version); @@ -106,12 +109,14 @@ namespace cryptonote { , crypto::hash8& payment_id , bool testnet , const std::string& str_or_url + , bool cli_confirm = true ); bool get_account_address_from_str_or_url( cryptonote::account_public_address& address , bool testnet , const std::string& str_or_url + , bool cli_confirm = true ); bool is_coinbase(const transaction& tx); @@ -120,23 +125,28 @@ namespace cryptonote { bool operator ==(const cryptonote::block& a, const cryptonote::block& b); } -template <class T> -std::ostream &print256(std::ostream &o, const T &v) { - return o << "<" << epee::string_tools::pod_to_hex(v) << ">"; -} -template <class T> -std::ostream &print64(std::ostream &o, const T &v) { - return o << "<" << epee::string_tools::pod_to_hex(v) << ">"; -} - bool parse_hash256(const std::string str_hash, crypto::hash& hash); namespace crypto { - inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { return print64(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; + } } diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 409b9798c..6e4ac9b72 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 2e6917878..745dfb72e 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,9 +31,9 @@ #include "include_base_utils.h" using namespace epee; +#include <atomic> #include "cryptonote_format_utils.h" #include "cryptonote_config.h" -#include "miner.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctSigs.h" @@ -43,6 +43,8 @@ using namespace epee; #define ENCRYPTED_PAYMENT_ID_TAIL 0x8d +// #define ENABLE_HASH_CASH_INTEGRITY_CHECK + static const uint64_t valid_decomposed_outputs[] = { (uint64_t)1, (uint64_t)2, (uint64_t)3, (uint64_t)4, (uint64_t)5, (uint64_t)6, (uint64_t)7, (uint64_t)8, (uint64_t)9, // 1 piconero (uint64_t)10, (uint64_t)20, (uint64_t)30, (uint64_t)40, (uint64_t)50, (uint64_t)60, (uint64_t)70, (uint64_t)80, (uint64_t)90, @@ -66,6 +68,13 @@ static const uint64_t valid_decomposed_outputs[] = { (uint64_t)10000000000000000000ull }; +static std::atomic<unsigned int> default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT); + +static std::atomic<uint64_t> tx_hashes_calculated_count(0); +static std::atomic<uint64_t> tx_hashes_cached_count(0); +static std::atomic<uint64_t> block_hashes_calculated_count(0); +static std::atomic<uint64_t> block_hashes_cached_count(0); + namespace cryptonote { //--------------------------------------------------------------- @@ -91,6 +100,17 @@ namespace cryptonote binary_archive<false> ba(ss); bool r = ::serialization::serialize(ba, tx); CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + tx.invalidate_hashes(); + return true; + } + //--------------------------------------------------------------- + bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx) + { + std::stringstream ss; + ss << tx_blob; + binary_archive<false> ba(ss); + bool r = tx.serialize_base(ba); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); return true; } //--------------------------------------------------------------- @@ -101,6 +121,7 @@ namespace cryptonote binary_archive<false> ba(ss); bool r = ::serialization::serialize(ba, tx); CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + tx.invalidate_hashes(); //TODO: validate tx get_transaction_hash(tx, tx_hash); @@ -108,102 +129,6 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { - tx.vin.clear(); - tx.vout.clear(); - tx.extra.clear(); - - keypair txkey = keypair::generate(); - add_tx_pub_key_to_extra(tx, txkey.pub); - if(!extra_nonce.empty()) - if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) - return false; - - txin_gen in; - in.height = height; - - uint64_t block_reward; - if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version)) - { - LOG_PRINT_L0("Block is too big"); - return false; - } - -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: reward " << block_reward << - ", fee " << fee); -#endif - block_reward += fee; - - // from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and - // keeps the paid amount almost the same. The unpaid remainder gets pushed back to the - // emission schedule - // from hard fork 4, we use a single "dusty" output. This makes the tx even smaller, - // and avoids the quantization. These outputs will be added as rct outputs with identity - // masks, to they can be used as rct inputs. - if (hard_fork_version >= 2 && hard_fork_version < 4) { - block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD; - } - - std::vector<uint64_t> out_amounts; - decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD, - [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, - [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); - - CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero"); - if (height == 0 || hard_fork_version >= 4) - { - // the genesis block was not decomposed, for unknown reasons - while (max_outs < out_amounts.size()) - { - //out_amounts[out_amounts.size() - 2] += out_amounts.back(); - //out_amounts.resize(out_amounts.size() - 1); - out_amounts[1] += out_amounts[0]; - for (size_t n = 1; n < out_amounts.size(); ++n) - out_amounts[n - 1] = out_amounts[n]; - out_amounts.resize(out_amounts.size() - 1); - } - } - else - { - CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded"); - } - - uint64_t summary_amounts = 0; - for (size_t no = 0; no < out_amounts.size(); no++) - { - crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);; - crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); - bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")"); - - r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")"); - - txout_to_key tk; - tk.key = out_eph_public_key; - - tx_out out; - summary_amounts += out.amount = out_amounts[no]; - out.target = tk; - tx.vout.push_back(out); - } - - CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); - - if (hard_fork_version >= 4) - tx.version = 2; - else - tx.version = 1; - - //lock - tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; - tx.vin.push_back(in); - //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee) - // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2); - return true; - } - //--------------------------------------------------------------- bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) { crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); @@ -239,12 +164,12 @@ namespace cryptonote if (std::string::npos != point_index) { fraction_size = str_amount.size() - point_index - 1; - while (CRYPTONOTE_DISPLAY_DECIMAL_POINT < fraction_size && '0' == str_amount.back()) + while (default_decimal_point < fraction_size && '0' == str_amount.back()) { str_amount.erase(str_amount.size() - 1, 1); --fraction_size; } - if (CRYPTONOTE_DISPLAY_DECIMAL_POINT < fraction_size) + if (default_decimal_point < fraction_size) return false; str_amount.erase(point_index, 1); } @@ -256,9 +181,9 @@ namespace cryptonote if (str_amount.empty()) return false; - if (fraction_size < CRYPTONOTE_DISPLAY_DECIMAL_POINT) + if (fraction_size < default_decimal_point) { - str_amount.append(CRYPTONOTE_DISPLAY_DECIMAL_POINT - fraction_size, '0'); + str_amount.append(default_decimal_point - fraction_size, '0'); } return string_tools::get_xtype_from_string(amount, str_amount); @@ -435,22 +360,6 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys) - { - if (destinations.empty()) - return null_pkey; - for (size_t n = 1; n < destinations.size(); ++n) - { - if (!memcmp(&destinations[n].addr, &sender_keys.m_account_address, sizeof(destinations[0].addr))) - continue; - if (destinations[n].amount == 0) - continue; - if (memcmp(&destinations[n].addr, &destinations[0].addr, sizeof(destinations[0].addr))) - return null_pkey; - } - return destinations[0].addr.m_view_public_key; - } - //--------------------------------------------------------------- bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) { crypto::key_derivation derivation; @@ -475,313 +384,6 @@ namespace cryptonote return encrypt_payment_id(payment_id, public_key, secret_key); } //--------------------------------------------------------------- - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct) - { - std::vector<rct::key> amount_keys; - tx.set_null(); - amount_keys.clear(); - - tx.version = rct ? 2 : 1; - tx.unlock_time = unlock_time; - - tx.extra = extra; - keypair txkey = keypair::generate(); - remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); - add_tx_pub_key_to_extra(tx, txkey.pub); - tx_key = txkey.sec; - - // if we have a stealth payment id, find it and encrypt it with the tx key now - std::vector<tx_extra_field> tx_extra_fields; - if (parse_tx_extra(tx.extra, tx_extra_fields)) - { - tx_extra_nonce extra_nonce; - if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) - { - crypto::hash8 payment_id = null_hash8; - if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) - { - LOG_PRINT_L2("Encrypting payment id " << payment_id); - crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, sender_account_keys); - if (view_key_pub == null_pkey) - { - LOG_ERROR("Destinations have to have exactly one output to support encrypted payment ids"); - return false; - } - - if (!encrypt_payment_id(payment_id, view_key_pub, txkey.sec)) - { - LOG_ERROR("Failed to encrypt payment id"); - return false; - } - - std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce)); - if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) - { - LOG_ERROR("Failed to add encrypted payment id to tx extra"); - return false; - } - LOG_PRINT_L1("Encrypted payment ID: " << payment_id); - } - } - } - else - { - LOG_ERROR("Failed to parse tx extra"); - return false; - } - - struct input_generation_context_data - { - keypair in_ephemeral; - }; - std::vector<input_generation_context_data> in_contexts; - - uint64_t summary_inputs_money = 0; - //fill inputs - int idx = -1; - for(const tx_source_entry& src_entr: sources) - { - ++idx; - if(src_entr.real_output >= src_entr.outputs.size()) - { - LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size()); - return false; - } - summary_inputs_money += src_entr.amount; - - //key_derivation recv_derivation; - in_contexts.push_back(input_generation_context_data()); - keypair& in_ephemeral = in_contexts.back().in_ephemeral; - crypto::key_image img; - if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) - return false; - - //check that derivated key is equal with real output key - if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) ) - { - LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:" - << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" - << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second) ); - LOG_ERROR("amount " << src_entr.amount << ", rct " << src_entr.rct); - LOG_ERROR("tx pubkey " << src_entr.real_out_tx_key << ", real_output_in_tx_index " << src_entr.real_output_in_tx_index); - return false; - } - - //put key image into tx input - txin_to_key input_to_key; - input_to_key.amount = src_entr.amount; - input_to_key.k_image = img; - - //fill outputs array and use relative offsets - for(const tx_source_entry::output_entry& out_entry: src_entr.outputs) - input_to_key.key_offsets.push_back(out_entry.first); - - input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); - tx.vin.push_back(input_to_key); - } - - // "Shuffle" outs - std::vector<tx_destination_entry> shuffled_dsts(destinations); - std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } ); - - uint64_t summary_outs_money = 0; - //fill outputs - size_t output_index = 0; - for(const tx_destination_entry& dst_entr: shuffled_dsts) - { - CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount); - crypto::key_derivation derivation; - crypto::public_key out_eph_public_key; - bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")"); - - if (tx.version > 1) - { - crypto::secret_key scalar1; - crypto::derivation_to_scalar(derivation, output_index, scalar1); - amount_keys.push_back(rct::sk2rct(scalar1)); - } - r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); - - tx_out out; - out.amount = dst_entr.amount; - txout_to_key tk; - tk.key = out_eph_public_key; - out.target = tk; - tx.vout.push_back(out); - output_index++; - summary_outs_money += dst_entr.amount; - } - - //check money - if(summary_outs_money > summary_inputs_money ) - { - LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"); - return false; - } - - // check for watch only wallet - bool zero_secret_key = true; - for (size_t i = 0; i < sizeof(sender_account_keys.m_spend_secret_key); ++i) - zero_secret_key &= (sender_account_keys.m_spend_secret_key.data[i] == 0); - if (zero_secret_key) - { - MDEBUG("Null secret key, skipping signatures"); - } - - if (tx.version == 1) - { - //generate ring signatures - crypto::hash tx_prefix_hash; - get_transaction_prefix_hash(tx, tx_prefix_hash); - - std::stringstream ss_ring_s; - size_t i = 0; - for(const tx_source_entry& src_entr: sources) - { - ss_ring_s << "pub_keys:" << ENDL; - std::vector<const crypto::public_key*> keys_ptrs; - std::vector<crypto::public_key> keys(src_entr.outputs.size()); - size_t ii = 0; - for(const tx_source_entry::output_entry& o: src_entr.outputs) - { - keys[ii] = rct2pk(o.second.dest); - keys_ptrs.push_back(&keys[ii]); - ss_ring_s << o.second.dest << ENDL; - ++ii; - } - - tx.signatures.push_back(std::vector<crypto::signature>()); - std::vector<crypto::signature>& sigs = tx.signatures.back(); - sigs.resize(src_entr.outputs.size()); - if (!zero_secret_key) - crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); - ss_ring_s << "signatures:" << ENDL; - std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;}); - ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output; - i++; - } - - MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str()); - } - else - { - size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct - - // the non-simple version is slightly smaller, but assumes all real inputs - // are on the same index, so can only be used if there just one ring. - bool use_simple_rct = sources.size() > 1; - - if (!use_simple_rct) - { - // non simple ringct requires all real inputs to be at the same index for all inputs - for(const tx_source_entry& src_entr: sources) - { - if(src_entr.real_output != sources.begin()->real_output) - { - LOG_ERROR("All inputs must have the same index for non-simple ringct"); - return false; - } - } - - // enforce same mixin for all outputs - for (size_t i = 1; i < sources.size(); ++i) { - if (n_total_outs != sources[i].outputs.size()) { - LOG_ERROR("Non-simple ringct transaction has varying mixin"); - return false; - } - } - } - - uint64_t amount_in = 0, amount_out = 0; - rct::ctkeyV inSk; - // mixRing indexing is done the other way round for simple - rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs); - rct::keyV destinations; - std::vector<uint64_t> inamounts, outamounts; - std::vector<unsigned int> index; - for (size_t i = 0; i < sources.size(); ++i) - { - rct::ctkey ctkey; - amount_in += sources[i].amount; - inamounts.push_back(sources[i].amount); - index.push_back(sources[i].real_output); - // inSk: (secret key, mask) - ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec); - ctkey.mask = sources[i].mask; - inSk.push_back(ctkey); - // inPk: (public key, commitment) - // will be done when filling in mixRing - } - for (size_t i = 0; i < tx.vout.size(); ++i) - { - destinations.push_back(rct::pk2rct(boost::get<txout_to_key>(tx.vout[i].target).key)); - outamounts.push_back(tx.vout[i].amount); - amount_out += tx.vout[i].amount; - } - - if (use_simple_rct) - { - // mixRing indexing is done the other way round for simple - for (size_t i = 0; i < sources.size(); ++i) - { - mixRing[i].resize(sources[i].outputs.size()); - for (size_t n = 0; n < sources[i].outputs.size(); ++n) - { - mixRing[i][n] = sources[i].outputs[n].second; - } - } - } - else - { - for (size_t i = 0; i < n_total_outs; ++i) // same index assumption - { - mixRing[i].resize(sources.size()); - for (size_t n = 0; n < sources.size(); ++n) - { - mixRing[i][n] = sources[n].outputs[i].second; - } - } - } - - // fee - if (!use_simple_rct && amount_in > amount_out) - outamounts.push_back(amount_in - amount_out); - - // zero out all amounts to mask rct outputs, real amounts are now encrypted - for (size_t i = 0; i < tx.vin.size(); ++i) - { - if (sources[i].rct) - boost::get<txin_to_key>(tx.vin[i]).amount = 0; - } - for (size_t i = 0; i < tx.vout.size(); ++i) - tx.vout[i].amount = 0; - - crypto::hash tx_prefix_hash; - get_transaction_prefix_hash(tx, tx_prefix_hash); - rct::ctkeyV outSk; - if (use_simple_rct) - tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk); - else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk); // same index assumption - - CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); - - MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL); - } - - return true; - } - //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) - { - crypto::secret_key tx_key; - return construct_tx_and_get_tx_key(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key); - } - //--------------------------------------------------------------- bool get_inputs_money_amount(const transaction& tx, uint64_t& money) { money = 0; @@ -924,14 +526,59 @@ namespace cryptonote cn_fast_hash(blob.data(), blob.size(), res); } //--------------------------------------------------------------- - std::string print_money(uint64_t amount) + void set_default_decimal_point(unsigned int decimal_point) { + switch (decimal_point) + { + case 12: + case 9: + case 6: + case 3: + case 0: + default_decimal_point = decimal_point; + break; + default: + ASSERT_MES_AND_THROW("Invalid decimal point specification: " << decimal_point); + } + } + //--------------------------------------------------------------- + unsigned int get_default_decimal_point() + { + return default_decimal_point; + } + //--------------------------------------------------------------- + std::string get_unit(unsigned int decimal_point) + { + if (decimal_point == (unsigned int)-1) + decimal_point = default_decimal_point; + switch (std::atomic_load(&default_decimal_point)) + { + case 12: + return "monero"; + case 9: + return "millinero"; + case 6: + return "micronero"; + case 3: + return "nanonero"; + case 0: + return "piconero"; + default: + ASSERT_MES_AND_THROW("Invalid decimal point specification: " << default_decimal_point); + } + } + //--------------------------------------------------------------- + std::string print_money(uint64_t amount, unsigned int decimal_point) + { + if (decimal_point == (unsigned int)-1) + decimal_point = default_decimal_point; std::string s = std::to_string(amount); - if(s.size() < CRYPTONOTE_DISPLAY_DECIMAL_POINT+1) + if(s.size() < decimal_point+1) { - s.insert(0, CRYPTONOTE_DISPLAY_DECIMAL_POINT+1 - s.size(), '0'); + s.insert(0, decimal_point+1 - s.size(), '0'); } - s.insert(s.size() - CRYPTONOTE_DISPLAY_DECIMAL_POINT, "."); + if (decimal_point > 0) + s.insert(s.size() - decimal_point, "."); return s; } //--------------------------------------------------------------- @@ -954,7 +601,7 @@ namespace cryptonote return get_transaction_hash(t, res, NULL); } //--------------------------------------------------------------- - bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) + bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) { // v1 transactions hash the entire blob if (t.version == 1) @@ -1009,6 +656,40 @@ namespace cryptonote return true; } //--------------------------------------------------------------- + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) + { + if (t.is_hash_valid()) + { +#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK + CHECK_AND_ASSERT_THROW_MES(!calculate_transaction_hash(t, res, blob_size) || t.hash == res, "tx hash cash integrity failure"); +#endif + res = t.hash; + if (blob_size) + { + if (!t.is_blob_size_valid()) + { + t.blob_size = get_object_blobsize(t); + t.set_blob_size_valid(true); + } + *blob_size = t.blob_size; + } + ++tx_hashes_cached_count; + return true; + } + ++tx_hashes_calculated_count; + bool ret = calculate_transaction_hash(t, res, blob_size); + if (!ret) + return false; + t.hash = res; + t.set_hash_valid(true); + if (blob_size) + { + t.blob_size = *blob_size; + t.set_blob_size_valid(true); + } + return true; + } + //--------------------------------------------------------------- bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size) { return get_transaction_hash(t, res, &blob_size); @@ -1023,7 +704,7 @@ namespace cryptonote return blob; } //--------------------------------------------------------------- - bool get_block_hash(const block& b, crypto::hash& res) + bool calculate_block_hash(const block& b, crypto::hash& res) { // EXCEPTION FOR BLOCK 202612 const std::string correct_blob_hash_202612 = "3a8a2b3a29b50fc86ff73dd087ea43c6f0d6b8f936c849194d5c84c737903966"; @@ -1050,6 +731,26 @@ namespace cryptonote return hash_result; } //--------------------------------------------------------------- + bool get_block_hash(const block& b, crypto::hash& res) + { + if (b.is_hash_valid()) + { +#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK + CHECK_AND_ASSERT_THROW_MES(!calculate_block_hash(b, res) || b.hash == res, "block hash cash integrity failure"); +#endif + res = b.hash; + ++block_hashes_cached_count; + return true; + } + ++block_hashes_calculated_count; + bool ret = calculate_block_hash(b, res); + if (!ret) + return false; + b.hash = res; + b.set_hash_valid(true); + return true; + } + //--------------------------------------------------------------- crypto::hash get_block_hash(const block& b) { crypto::hash p = null_hash; @@ -1057,36 +758,6 @@ namespace cryptonote return p; } //--------------------------------------------------------------- - bool generate_genesis_block( - block& bl - , std::string const & genesis_tx - , uint32_t nonce - ) - { - //genesis block - bl = boost::value_initialized<block>(); - - - account_public_address ac = boost::value_initialized<account_public_address>(); - std::vector<size_t> sz; - construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis - blobdata txb = tx_to_blob(bl.miner_tx); - std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); - - std::string genesis_coinbase_tx_hex = config::GENESIS_TX; - - blobdata tx_bl; - string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); - bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); - CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); - bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; - bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; - bl.timestamp = 0; - bl.nonce = nonce; - miner::find_nonce_for_given_block(bl, 1, 0); - return true; - } - //--------------------------------------------------------------- bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) { // block 202612 bug workaround @@ -1096,7 +767,6 @@ namespace cryptonote string_tools::hex_to_pod(longhash_202612, res); return true; } - block b_local = b; //workaround to avoid const errors with do_serialize blobdata bd = get_block_hashing_blob(b); crypto::cn_slow_hash(bd.data(), bd.size(), res); return true; @@ -1136,6 +806,8 @@ namespace cryptonote binary_archive<false> ba(ss); bool r = ::serialization::serialize(ba, b); CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob"); + b.invalidate_hashes(); + b.miner_tx.invalidate_hashes(); return true; } //--------------------------------------------------------------- @@ -1190,4 +862,11 @@ namespace cryptonote return std::binary_search(begin, end, amount); } //--------------------------------------------------------------- + void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached) + { + tx_hashes_calculated = tx_hashes_calculated_count; + tx_hashes_cached = tx_hashes_cached_count; + block_hashes_calculated = block_hashes_calculated_count; + block_hashes_cached = block_hashes_cached_count; + } } diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index a6610e60d..5c10907fd 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,14 +30,12 @@ #pragma once #include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic_impl.h" #include "account.h" #include "include_base_utils.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "ringct/rctOps.h" -#include <boost/serialization/vector.hpp> -#include <boost/serialization/utility.hpp> namespace cryptonote { @@ -46,43 +44,10 @@ namespace cryptonote crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx); - bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); + bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx); bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key); bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key); - struct tx_source_entry - { - typedef std::pair<uint64_t, rct::ctkey> output_entry; - - std::vector<output_entry> outputs; //index + key + optional ringct commitment - size_t real_output; //index in outputs vector of real output_entry - crypto::public_key real_out_tx_key; //incoming real tx public key - size_t real_output_in_tx_index; //index in transaction outputs vector - uint64_t amount; //money - bool rct; //true if the output is rct - rct::key mask; //ringct amount mask - - void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); } - }; - - struct tx_destination_entry - { - uint64_t amount; //money - account_public_address addr; //destination address - - tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)) { } - tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { } - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(amount) - FIELD(addr) - END_SERIALIZE() - }; - - //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); - bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false); - template<typename T> bool find_tx_extra_field_by_type(const std::vector<tx_extra_field>& tx_extra_fields, T& field, size_t index = 0) { @@ -120,16 +85,13 @@ namespace cryptonote bool get_transaction_hash(const transaction& t, crypto::hash& res); bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size); bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size); + bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size); blobdata get_block_hashing_blob(const block& b); + bool calculate_block_hash(const block& b, crypto::hash& res); bool get_block_hash(const block& b, crypto::hash& res); crypto::hash get_block_hash(const block& b); bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height); crypto::hash get_block_longhash(const block& b, uint64_t height); - bool generate_genesis_block( - block& bl - , std::string const & genesis_tx - , uint32_t nonce - ); bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); bool get_inputs_money_amount(const transaction& tx, uint64_t& money); uint64_t get_outs_money_amount(const transaction& tx); @@ -143,7 +105,10 @@ namespace cryptonote uint64_t get_block_height(const block& b); std::vector<uint64_t> relative_output_offsets_to_absolute(const std::vector<uint64_t>& off); std::vector<uint64_t> absolute_output_offsets_to_relative(const std::vector<uint64_t>& off); - std::string print_money(uint64_t amount); + void set_default_decimal_point(unsigned int decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT); + unsigned int get_default_decimal_point(); + std::string get_unit(unsigned int decimal_point = -1); + std::string print_money(uint64_t amount, unsigned int decimal_point = -1); //--------------------------------------------------------------- template<class t_object> bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob) @@ -246,29 +211,10 @@ namespace cryptonote crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes); crypto::hash get_tx_tree_hash(const block& b); bool is_valid_decomposed_amount(uint64_t amount); + void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached); #define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ specific_type& variable_name = boost::get<specific_type>(variant_var); } - -BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0) - -namespace boost -{ - namespace serialization - { - template <class Archive> - inline void serialize(Archive &a, cryptonote::tx_source_entry &x, const boost::serialization::version_type ver) - { - a & x.outputs; - a & x.real_output; - a & x.real_out_tx_key; - a & x.real_output_in_tx_index; - a & x.amount; - a & x.rct; - a & x.mask; - } - } -} diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/cryptonote_basic/cryptonote_stat_info.h index d44904b6d..7ebf86878 100644 --- a/src/cryptonote_core/cryptonote_stat_info.h +++ b/src/cryptonote_basic/cryptonote_stat_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 1c5b9cfbd..863aa4359 100644 --- a/src/cryptonote_core/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/difficulty.h b/src/cryptonote_basic/difficulty.h index 910f97035..aed6cb289 100644 --- a/src/cryptonote_core/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index 13d7d717d..546af2076 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ #include <algorithm> #include <cstdio> -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "blockchain_db/blockchain_db.h" #include "hardfork.h" @@ -191,10 +191,10 @@ void HardFork::init() } catch (...) { populate = true; } if (populate) { - LOG_PRINT_L0("The DB has no hard fork info, reparsing from start"); + MINFO("The DB has no hard fork info, reparsing from start"); height = 1; } - LOG_PRINT_L1("reorganizing from " << height); + MDEBUG("reorganizing from " << height); if (populate) { reorganize_from_chain_height(height); // reorg will not touch the genesis block, use this as a flag for populating done @@ -203,7 +203,7 @@ void HardFork::init() else { rescan_from_chain_height(height); } - LOG_PRINT_L1("reorganization done"); + MDEBUG("reorganization done"); } uint8_t HardFork::get_block_version(uint64_t height) const @@ -222,7 +222,7 @@ bool HardFork::reorganize_from_block_height(uint64_t height) return false; db.set_batch_transactions(true); - db.batch_start(); + bool stop_batch = db.batch_start(); versions.clear(); @@ -250,7 +250,8 @@ bool HardFork::reorganize_from_block_height(uint64_t height) add(db.get_block_from_height(h), h); } - db.batch_stop(); + if (stop_batch) + db.batch_stop(); return true; } diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_basic/hardfork.h index 8d2edfa2a..6c6fbcb84 100644 --- a/src/cryptonote_core/hardfork.h +++ b/src/cryptonote_basic/hardfork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ #pragma once #include "syncobj.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" namespace cryptonote { diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp new file mode 100644 index 000000000..eeb7b6094 --- /dev/null +++ b/src/cryptonote_basic/miner.cpp @@ -0,0 +1,853 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include <sstream> +#include <numeric> +#include <boost/utility/value_init.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/limits.hpp> +#include "misc_language.h" +#include "include_base_utils.h" +#include "cryptonote_basic_impl.h" +#include "cryptonote_format_utils.h" +#include "file_io_utils.h" +#include "common/command_line.h" +#include "string_coding.h" +#include "storages/portable_storage_template_helper.h" +#include "boost/logic/tribool.hpp" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "miner" + +using namespace epee; + +#include "miner.h" + + +extern "C" void slow_hash_allocate_state(); +extern "C" void slow_hash_free_state(); +namespace cryptonote +{ + + namespace + { + const command_line::arg_descriptor<std::string> arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; + const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; + const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; + const command_line::arg_descriptor<bool> arg_bg_mining_enable = {"bg-mining-enable", "enable/disable background mining", true, true}; + const command_line::arg_descriptor<bool> arg_bg_mining_ignore_battery = {"bg-mining-ignore-battery", "if true, assumes plugged in when unable to query system power status", false, true}; + const command_line::arg_descriptor<uint64_t> arg_bg_mining_min_idle_interval_seconds = {"bg-mining-min-idle-interval", "Specify min lookback interval in seconds for determining idle state", miner::BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS, true}; + const command_line::arg_descriptor<uint8_t> arg_bg_mining_idle_threshold_percentage = {"bg-mining-idle-threshold", "Specify minimum avg idle percentage over lookback interval", miner::BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE, true}; + const command_line::arg_descriptor<uint8_t> arg_bg_mining_miner_target_percentage = {"bg-mining-miner-target", "Specificy maximum percentage cpu use by miner(s)", miner::BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE, true}; + } + + + miner::miner(i_miner_handler* phandler):m_stop(1), + m_template(boost::value_initialized<block>()), + m_template_no(0), + m_diffic(0), + m_thread_index(0), + m_phandler(phandler), + m_height(0), + m_pausers_count(0), + m_threads_total(0), + m_starter_nonce(0), + m_last_hr_merge_time(0), + m_hashes(0), + m_do_print_hashrate(false), + m_do_mining(false), + m_current_hash_rate(0), + m_is_background_mining_enabled(false), + m_min_idle_seconds(BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS), + m_idle_threshold(BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE), + m_mining_target(BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE), + m_miner_extra_sleep(BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS) + { + + } + //----------------------------------------------------------------------------------------------------- + miner::~miner() + { + stop(); + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height) + { + CRITICAL_REGION_LOCAL(m_template_lock); + m_template = bl; + m_diffic = di; + m_height = height; + ++m_template_no; + m_starter_nonce = crypto::rand<uint32_t>(); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::on_block_chain_update() + { + if(!is_mining()) + return true; + + return request_block_template(); + } + //----------------------------------------------------------------------------------------------------- + bool miner::request_block_template() + { + block bl = AUTO_VAL_INIT(bl); + difficulty_type di = AUTO_VAL_INIT(di); + uint64_t height = AUTO_VAL_INIT(height); + uint64_t expected_reward; //only used for RPC calls - could possibly be useful here too? + + cryptonote::blobdata extra_nonce; + if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) + { + extra_nonce = m_extra_messages[m_config.current_extra_message_index]; + } + + if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce)) + { + LOG_ERROR("Failed to get_block_template(), stopping mining"); + return false; + } + set_block_template(bl, di, height); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::on_idle() + { + m_update_block_template_interval.do_call([&](){ + if(is_mining())request_block_template(); + return true; + }); + + m_update_merge_hr_interval.do_call([&](){ + merge_hr(); + return true; + }); + + return true; + } + //----------------------------------------------------------------------------------------------------- + void miner::do_print_hashrate(bool do_hr) + { + m_do_print_hashrate = do_hr; + } + //----------------------------------------------------------------------------------------------------- + void miner::merge_hr() + { + if(m_last_hr_merge_time && is_mining()) + { + m_current_hash_rate = m_hashes * 1000 / ((misc_utils::get_tick_count() - m_last_hr_merge_time + 1)); + CRITICAL_REGION_LOCAL(m_last_hash_rates_lock); + m_last_hash_rates.push_back(m_current_hash_rate); + if(m_last_hash_rates.size() > 19) + m_last_hash_rates.pop_front(); + if(m_do_print_hashrate) + { + uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0); + float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size()); + std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << ENDL; + } + } + m_last_hr_merge_time = misc_utils::get_tick_count(); + m_hashes = 0; + } + //----------------------------------------------------------------------------------------------------- + void miner::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_extra_messages); + command_line::add_arg(desc, arg_start_mining); + command_line::add_arg(desc, arg_mining_threads); + command_line::add_arg(desc, arg_bg_mining_enable); + command_line::add_arg(desc, arg_bg_mining_ignore_battery); + command_line::add_arg(desc, arg_bg_mining_min_idle_interval_seconds); + command_line::add_arg(desc, arg_bg_mining_idle_threshold_percentage); + command_line::add_arg(desc, arg_bg_mining_miner_target_percentage); + } + //----------------------------------------------------------------------------------------------------- + bool miner::init(const boost::program_options::variables_map& vm, bool testnet) + { + if(command_line::has_arg(vm, arg_extra_messages)) + { + std::string buff; + bool r = file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_extra_messages), buff); + CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << command_line::get_arg(vm, arg_extra_messages)); + std::vector<std::string> extra_vec; + boost::split(extra_vec, buff, boost::is_any_of("\n"), boost::token_compress_on ); + m_extra_messages.resize(extra_vec.size()); + for(size_t i = 0; i != extra_vec.size(); i++) + { + string_tools::trim(extra_vec[i]); + if(!extra_vec[i].size()) + continue; + std::string buff = string_encoding::base64_decode(extra_vec[i]); + if(buff != "0") + m_extra_messages[i] = buff; + } + m_config_folder_path = boost::filesystem::path(command_line::get_arg(vm, arg_extra_messages)).parent_path().string(); + m_config = AUTO_VAL_INIT(m_config); + epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); + MINFO("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index); + } + + if(command_line::has_arg(vm, arg_start_mining)) + { + if(!cryptonote::get_account_address_from_str(m_mine_address, testnet, command_line::get_arg(vm, arg_start_mining))) + { + LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled"); + return false; + } + m_threads_total = 1; + m_do_mining = true; + if(command_line::has_arg(vm, arg_mining_threads)) + { + m_threads_total = command_line::get_arg(vm, arg_mining_threads); + } + } + + // Background mining parameters + // Let init set all parameters even if background mining is not enabled, they can start later with params set + if(command_line::has_arg(vm, arg_bg_mining_enable)) + set_is_background_mining_enabled( command_line::get_arg(vm, arg_bg_mining_enable) ); + if(command_line::has_arg(vm, arg_bg_mining_ignore_battery)) + set_ignore_battery( command_line::get_arg(vm, arg_bg_mining_ignore_battery) ); + if(command_line::has_arg(vm, arg_bg_mining_min_idle_interval_seconds)) + set_min_idle_seconds( command_line::get_arg(vm, arg_bg_mining_min_idle_interval_seconds) ); + if(command_line::has_arg(vm, arg_bg_mining_idle_threshold_percentage)) + set_idle_threshold( command_line::get_arg(vm, arg_bg_mining_idle_threshold_percentage) ); + if(command_line::has_arg(vm, arg_bg_mining_miner_target_percentage)) + set_mining_target( command_line::get_arg(vm, arg_bg_mining_miner_target_percentage) ); + + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::is_mining() const + { + return !m_stop; + } + //----------------------------------------------------------------------------------------------------- + const account_public_address& miner::get_mining_address() const + { + return m_mine_address; + } + //----------------------------------------------------------------------------------------------------- + uint32_t miner::get_threads_count() const { + return m_threads_total; + } + //----------------------------------------------------------------------------------------------------- + bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background, bool ignore_battery) + { + m_mine_address = adr; + m_threads_total = static_cast<uint32_t>(threads_count); + m_starter_nonce = crypto::rand<uint32_t>(); + CRITICAL_REGION_LOCAL(m_threads_lock); + if(is_mining()) + { + LOG_ERROR("Starting miner but it's already started"); + return false; + } + + if(!m_threads.empty()) + { + LOG_ERROR("Unable to start miner because there are active mining threads"); + return false; + } + + if(!m_template_no) + request_block_template();//lets update block template + + boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0); + boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0); + set_is_background_mining_enabled(do_background); + set_ignore_battery(ignore_battery); + + for(size_t i = 0; i != threads_count; i++) + { + m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this))); + } + + LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" ); + + if( get_is_background_mining_enabled() ) + { + m_background_mining_thread = boost::thread(attrs, boost::bind(&miner::background_worker_thread, this)); + LOG_PRINT_L0("Background mining controller thread started" ); + } + + return true; + } + //----------------------------------------------------------------------------------------------------- + uint64_t miner::get_speed() const + { + if(is_mining()) { + return m_current_hash_rate; + } + else { + return 0; + } + } + //----------------------------------------------------------------------------------------------------- + void miner::send_stop_signal() + { + boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1); + } + //----------------------------------------------------------------------------------------------------- + bool miner::stop() + { + MTRACE("Miner has received stop signal"); + + if (!is_mining()) + { + MDEBUG("Not mining - nothing to stop" ); + return true; + } + + send_stop_signal(); + CRITICAL_REGION_LOCAL(m_threads_lock); + + // In case background mining was active and the miner threads are waiting + // on the background miner to signal start. + m_is_background_mining_started_cond.notify_all(); + + for(boost::thread& th: m_threads) + th.join(); + + // The background mining thread could be sleeping for a long time, so we + // interrupt it just in case + m_background_mining_thread.interrupt(); + m_background_mining_thread.join(); + + MINFO("Mining has been stopped, " << m_threads.size() << " finished" ); + m_threads.clear(); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height) + { + for(; bl.nonce != std::numeric_limits<uint32_t>::max(); bl.nonce++) + { + crypto::hash h; + get_block_longhash(bl, h, height); + + if(check_hash(h, diffic)) + { + bl.invalidate_hashes(); + return true; + } + } + bl.invalidate_hashes(); + return false; + } + //----------------------------------------------------------------------------------------------------- + void miner::on_synchronized() + { + if(m_do_mining) + { + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); + + start(m_mine_address, m_threads_total, attrs, get_is_background_mining_enabled()); + } + } + //----------------------------------------------------------------------------------------------------- + void miner::pause() + { + CRITICAL_REGION_LOCAL(m_miners_count_lock); + MDEBUG("miner::pause: " << m_pausers_count << " -> " << (m_pausers_count + 1)); + ++m_pausers_count; + if(m_pausers_count == 1 && is_mining()) + MDEBUG("MINING PAUSED"); + } + //----------------------------------------------------------------------------------------------------- + void miner::resume() + { + CRITICAL_REGION_LOCAL(m_miners_count_lock); + MDEBUG("miner::resume: " << m_pausers_count << " -> " << (m_pausers_count - 1)); + --m_pausers_count; + if(m_pausers_count < 0) + { + m_pausers_count = 0; + MERROR("Unexpected miner::resume() called"); + } + if(!m_pausers_count && is_mining()) + MDEBUG("MINING RESUMED"); + } + //----------------------------------------------------------------------------------------------------- + bool miner::worker_thread() + { + uint32_t th_local_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index); + MGINFO("Miner thread was started ["<< th_local_index << "]"); + MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(th_local_index) + "]"); + uint32_t nonce = m_starter_nonce + th_local_index; + uint64_t height = 0; + difficulty_type local_diff = 0; + uint32_t local_template_ver = 0; + block b; + slow_hash_allocate_state(); + while(!m_stop) + { + if(m_pausers_count)//anti split workaround + { + misc_utils::sleep_no_w(100); + continue; + } + else if( m_is_background_mining_enabled ) + { + misc_utils::sleep_no_w(m_miner_extra_sleep); + while( !m_is_background_mining_started ) + { + MGINFO("background mining is enabled, but not started, waiting until start triggers"); + boost::unique_lock<boost::mutex> started_lock( m_is_background_mining_started_mutex ); + m_is_background_mining_started_cond.wait( started_lock ); + if( m_stop ) break; + } + + if( m_stop ) continue; + } + + if(local_template_ver != m_template_no) + { + CRITICAL_REGION_BEGIN(m_template_lock); + b = m_template; + local_diff = m_diffic; + height = m_height; + CRITICAL_REGION_END(); + local_template_ver = m_template_no; + nonce = m_starter_nonce + th_local_index; + } + + if(!local_template_ver)//no any set_block_template call + { + LOG_PRINT_L2("Block template not set yet"); + epee::misc_utils::sleep_no_w(1000); + continue; + } + + b.nonce = nonce; + crypto::hash h; + get_block_longhash(b, h, height); + + if(check_hash(h, local_diff)) + { + //we lucky! + ++m_config.current_extra_message_index; + MGINFO_GREEN("Found block for difficulty: " << local_diff); + if(!m_phandler->handle_block_found(b)) + { + --m_config.current_extra_message_index; + }else + { + //success update, lets update config + if (!m_config_folder_path.empty()) + epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); + } + } + nonce+=m_threads_total; + ++m_hashes; + } + slow_hash_free_state(); + MGINFO("Miner thread stopped ["<< th_local_index << "]"); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::get_is_background_mining_enabled() const + { + return m_is_background_mining_enabled; + } + //----------------------------------------------------------------------------------------------------- + bool miner::get_ignore_battery() const + { + return m_ignore_battery; + } + //----------------------------------------------------------------------------------------------------- + /** + * This has differing behaviour depending on if mining has been started/etc. + * Note: add documentation + */ + bool miner::set_is_background_mining_enabled(bool is_background_mining_enabled) + { + m_is_background_mining_enabled = is_background_mining_enabled; + // Extra logic will be required if we make this function public in the future + // and allow toggling smart mining without start/stop + //m_is_background_mining_enabled_cond.notify_one(); + return true; + } + //----------------------------------------------------------------------------------------------------- + void miner::set_ignore_battery(bool ignore_battery) + { + m_ignore_battery = ignore_battery; + } + //----------------------------------------------------------------------------------------------------- + uint64_t miner::get_min_idle_seconds() const + { + return m_min_idle_seconds; + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_min_idle_seconds(uint64_t min_idle_seconds) + { + if(min_idle_seconds > BACKGROUND_MINING_MAX_MIN_IDLE_INTERVAL_IN_SECONDS) return false; + if(min_idle_seconds < BACKGROUND_MINING_MIN_MIN_IDLE_INTERVAL_IN_SECONDS) return false; + m_min_idle_seconds = min_idle_seconds; + return true; + } + //----------------------------------------------------------------------------------------------------- + uint8_t miner::get_idle_threshold() const + { + return m_idle_threshold; + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_idle_threshold(uint8_t idle_threshold) + { + if(idle_threshold > BACKGROUND_MINING_MAX_IDLE_THRESHOLD_PERCENTAGE) return false; + if(idle_threshold < BACKGROUND_MINING_MIN_IDLE_THRESHOLD_PERCENTAGE) return false; + m_idle_threshold = idle_threshold; + return true; + } + //----------------------------------------------------------------------------------------------------- + uint8_t miner::get_mining_target() const + { + return m_mining_target; + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_mining_target(uint8_t mining_target) + { + if(mining_target > BACKGROUND_MINING_MAX_MINING_TARGET_PERCENTAGE) return false; + if(mining_target < BACKGROUND_MINING_MIN_MINING_TARGET_PERCENTAGE) return false; + m_mining_target = mining_target; + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::background_worker_thread() + { + uint64_t prev_total_time, current_total_time; + uint64_t prev_idle_time, current_idle_time; + uint64_t previous_process_time = 0, current_process_time = 0; + m_is_background_mining_started = false; + + if(!get_system_times(prev_total_time, prev_idle_time)) + { + LOG_ERROR("get_system_times call failed, background mining will NOT work!"); + return false; + } + + while(!m_stop) + { + + try + { + // Commenting out the below since we're going with privatizing the bg mining enabled + // function, but I'll leave the code/comments here for anyone that wants to modify the + // patch in the future + // ------------------------------------------------------------------------------------- + // All of this might be overkill if we just enforced some simple requirements + // about changing this variable before/after the miner starts, but I envision + // in the future a checkbox that you can tick on/off for background mining after + // you've clicked "start mining". There's still an issue here where if background + // mining is disabled when start is called, this thread is never created, and so + // enabling after does nothing, something I have to fix in the future. However, + // this should take care of the case where mining is started with bg-enabled, + // and then the user decides to un-check background mining, and just do + // regular full-speed mining. I might just be over-doing it and thinking up + // non-existant use-cases, so if the concensus is to simplify, we can remove all this fluff. + /* + while( !m_is_background_mining_enabled ) + { + MGINFO("background mining is disabled, waiting until enabled!"); + boost::unique_lock<boost::mutex> enabled_lock( m_is_background_mining_enabled_mutex ); + m_is_background_mining_enabled_cond.wait( enabled_lock ); + } + */ + + // If we're already mining, then sleep for the miner monitor interval. + // If we're NOT mining, then sleep for the idle monitor interval + uint64_t sleep_for_seconds = BACKGROUND_MINING_MINER_MONITOR_INVERVAL_IN_SECONDS; + if( !m_is_background_mining_started ) sleep_for_seconds = get_min_idle_seconds(); + boost::this_thread::sleep_for(boost::chrono::seconds(sleep_for_seconds)); + } + catch(const boost::thread_interrupted&) + { + MDEBUG("background miner thread interrupted "); + continue; // if interrupted because stop called, loop should end .. + } + + boost::tribool battery_powered(on_battery_power()); + bool on_ac_power = false; + if(indeterminate( battery_powered )) + { + // name could be better, only ignores battery requirement if we failed + // to get the status of the system + if( m_ignore_battery ) + { + on_ac_power = true; + } + } + else + { + on_ac_power = !battery_powered; + } + + if( m_is_background_mining_started ) + { + // figure out if we need to stop, and monitor mining usage + + // If we get here, then previous values are initialized. + // Let's get some current data for comparison. + + if(!get_system_times(current_total_time, current_idle_time)) + { + MERROR("get_system_times call failed"); + continue; + } + + if(!get_process_time(current_process_time)) + { + MERROR("get_process_time call failed!"); + continue; + } + + uint64_t total_diff = (current_total_time - prev_total_time); + uint64_t idle_diff = (current_idle_time - prev_idle_time); + uint64_t process_diff = (current_process_time - previous_process_time); + uint8_t idle_percentage = get_percent_of_total(idle_diff, total_diff); + uint8_t process_percentage = get_percent_of_total(process_diff, total_diff); + + MGINFO("idle percentage is " << unsigned(idle_percentage) << "\%, miner percentage is " << unsigned(process_percentage) << "\%, ac power : " << on_ac_power); + if( idle_percentage + process_percentage < get_idle_threshold() || !on_ac_power ) + { + MGINFO("cpu is " << unsigned(idle_percentage) << "% idle, idle threshold is " << unsigned(get_idle_threshold()) << "\%, ac power : " << on_ac_power << ", background mining stopping, thanks for your contribution!"); + m_is_background_mining_started = false; + + // reset process times + previous_process_time = 0; + current_process_time = 0; + } + else + { + previous_process_time = current_process_time; + + // adjust the miner extra sleep variable + int64_t miner_extra_sleep_change = (-1 * (get_mining_target() - process_percentage) ); + int64_t new_miner_extra_sleep = m_miner_extra_sleep + miner_extra_sleep_change; + // if you start the miner with few threads on a multicore system, this could + // fall below zero because all the time functions aggregate across all processors. + // I'm just hard limiting to 5 millis min sleep here, other options? + m_miner_extra_sleep = std::max( new_miner_extra_sleep , (int64_t)5 ); + MDEBUG("m_miner_extra_sleep " << m_miner_extra_sleep); + } + + prev_total_time = current_total_time; + prev_idle_time = current_idle_time; + } + else if( on_ac_power ) + { + // figure out if we need to start + + if(!get_system_times(current_total_time, current_idle_time)) + { + MERROR("get_system_times call failed"); + continue; + } + + uint64_t total_diff = (current_total_time - prev_total_time); + uint64_t idle_diff = (current_idle_time - prev_idle_time); + uint8_t idle_percentage = get_percent_of_total(idle_diff, total_diff); + + MGINFO("idle percentage is " << unsigned(idle_percentage)); + if( idle_percentage >= get_idle_threshold() && on_ac_power ) + { + MGINFO("cpu is " << unsigned(idle_percentage) << "% idle, idle threshold is " << unsigned(get_idle_threshold()) << "\%, ac power : " << on_ac_power << ", background mining started, good luck!"); + m_is_background_mining_started = true; + m_is_background_mining_started_cond.notify_all(); + + // Wait for a little mining to happen .. + boost::this_thread::sleep_for(boost::chrono::seconds( 1 )); + + // Starting data ... + if(!get_process_time(previous_process_time)) + { + m_is_background_mining_started = false; + MERROR("get_process_time call failed!"); + } + } + + prev_total_time = current_total_time; + prev_idle_time = current_idle_time; + } + } + + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::get_system_times(uint64_t& total_time, uint64_t& idle_time) + { + #ifdef _WIN32 + + FILETIME idleTime; + FILETIME kernelTime; + FILETIME userTime; + if ( GetSystemTimes( &idleTime, &kernelTime, &userTime ) != -1 ) + { + total_time = + ( (((uint64_t)(kernelTime.dwHighDateTime)) << 32) | ((uint64_t)kernelTime.dwLowDateTime) ) + + ( (((uint64_t)(userTime.dwHighDateTime)) << 32) | ((uint64_t)userTime.dwLowDateTime) ); + + idle_time = ( (((uint64_t)(idleTime.dwHighDateTime)) << 32) | ((uint64_t)idleTime.dwLowDateTime) ); + + return true; + } + + #elif defined(__linux__) + + const std::string STR_CPU("cpu"); + const std::size_t STR_CPU_LEN = STR_CPU.size(); + const std::string STAT_FILE_PATH = "/proc/stat"; + + if( !epee::file_io_utils::is_file_exist(STAT_FILE_PATH) ) + { + LOG_ERROR("'" << STAT_FILE_PATH << "' file does not exist"); + return false; + } + + std::ifstream stat_file_stream(STAT_FILE_PATH); + if( stat_file_stream.fail() ) + { + LOG_ERROR("failed to open '" << STAT_FILE_PATH << "'"); + return false; + } + + std::string line; + std::getline(stat_file_stream, line); + std::istringstream stat_file_iss(line); + stat_file_iss.ignore(65536, ' '); // skip cpu label ... + uint64_t utime, ntime, stime, itime; + if( !(stat_file_iss >> utime && stat_file_iss >> ntime && stat_file_iss >> stime && stat_file_iss >> itime) ) + { + LOG_ERROR("failed to read '" << STAT_FILE_PATH << "'"); + return false; + } + + idle_time = itime; + total_time = utime + ntime + stime + itime; + + return true; + + #endif + + return false; // unsupported systemm.. + } + //----------------------------------------------------------------------------------------------------- + bool miner::get_process_time(uint64_t& total_time) + { + #ifdef _WIN32 + + FILETIME createTime; + FILETIME exitTime; + FILETIME kernelTime; + FILETIME userTime; + if ( GetProcessTimes( GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime ) != -1 ) + { + total_time = + ( (((uint64_t)(kernelTime.dwHighDateTime)) << 32) | ((uint64_t)kernelTime.dwLowDateTime) ) + + ( (((uint64_t)(userTime.dwHighDateTime)) << 32) | ((uint64_t)userTime.dwLowDateTime) ); + + return true; + } + + #elif defined(__linux__) && defined(_SC_CLK_TCK) + + struct tms tms; + if ( times(&tms) != (clock_t)-1 ) + { + total_time = tms.tms_utime + tms.tms_stime; + return true; + } + + #endif + + return false; // unsupported system.. + } + //----------------------------------------------------------------------------------------------------- + uint8_t miner::get_percent_of_total(uint64_t other, uint64_t total) + { + return (uint8_t)( ceil( (other * 1.f / total * 1.f) * 100) ); + } + //----------------------------------------------------------------------------------------------------- + boost::logic::tribool miner::on_battery_power() + { + #ifdef _WIN32 + + SYSTEM_POWER_STATUS power_status; + if ( GetSystemPowerStatus( &power_status ) != 0 ) + { + return boost::logic::tribool(power_status.ACLineStatus != 1); + } + + #elif defined(__linux__) + + // i've only tested on UBUNTU, these paths might be different on other systems + // need to figure out a way to make this more flexible + std::string power_supply_path = ""; + const std::string POWER_SUPPLY_STATUS_PATHS[] = + { + "/sys/class/power_supply/ACAD/online", + "/sys/class/power_supply/AC/online", + "/sys/class/power_supply/AC0/online", + "/sys/class/power_supply/ADP0/online" + }; + + for(const std::string& path : POWER_SUPPLY_STATUS_PATHS) + { + if( epee::file_io_utils::is_file_exist(path) ) + { + power_supply_path = path; + break; + } + } + + if( power_supply_path.empty() ) + { + LOG_ERROR("Couldn't find battery/power status file, can't determine if plugged in!"); + return boost::logic::tribool(boost::logic::indeterminate);; + } + + std::ifstream power_stream(power_supply_path); + if( power_stream.fail() ) + { + LOG_ERROR("failed to open '" << power_supply_path << "'"); + return boost::logic::tribool(boost::logic::indeterminate);; + } + + return boost::logic::tribool( (power_stream.get() != '1') ); + + #endif + + LOG_ERROR("couldn't query power status"); + return boost::logic::tribool(boost::logic::indeterminate); + } +} diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_basic/miner.h index f24f6e960..964ee6a36 100644 --- a/src/cryptonote_core/miner.h +++ b/src/cryptonote_basic/miner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,11 +31,19 @@ #pragma once #include <boost/program_options.hpp> +#include <boost/logic/tribool_fwd.hpp> #include <atomic> #include "cryptonote_basic.h" #include "difficulty.h" #include "math_helper.h" - +#ifdef _WIN32 +#include <windows.h> +#elif defined(__linux__) +#include <unistd.h> +#include <sys/resource.h> +#include <sys/times.h> +#include <time.h> +#endif namespace cryptonote { @@ -43,7 +51,7 @@ namespace cryptonote struct i_miner_handler { virtual bool handle_block_found(block& b) = 0; - virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) = 0; + virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) = 0; protected: ~i_miner_handler(){}; }; @@ -60,7 +68,7 @@ namespace cryptonote static void init_options(boost::program_options::options_description& desc); bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height); bool on_block_chain_update(); - bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs); + bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background = false, bool ignore_battery = false); uint64_t get_speed() const; uint32_t get_threads_count() const; void send_stop_signal(); @@ -74,6 +82,27 @@ namespace cryptonote void pause(); void resume(); void do_print_hashrate(bool do_hr); + bool get_is_background_mining_enabled() const; + bool get_ignore_battery() const; + uint64_t get_min_idle_seconds() const; + bool set_min_idle_seconds(uint64_t min_idle_seconds); + uint8_t get_idle_threshold() const; + bool set_idle_threshold(uint8_t idle_threshold); + uint8_t get_mining_target() const; + bool set_mining_target(uint8_t mining_target); + + static constexpr uint8_t BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE = 90; + static constexpr uint8_t BACKGROUND_MINING_MIN_IDLE_THRESHOLD_PERCENTAGE = 50; + static constexpr uint8_t BACKGROUND_MINING_MAX_IDLE_THRESHOLD_PERCENTAGE = 99; + static constexpr uint16_t BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS = 10; + static constexpr uint16_t BACKGROUND_MINING_MIN_MIN_IDLE_INTERVAL_IN_SECONDS = 10; + static constexpr uint16_t BACKGROUND_MINING_MAX_MIN_IDLE_INTERVAL_IN_SECONDS = 3600; + static constexpr uint8_t BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE = 40; + static constexpr uint8_t BACKGROUND_MINING_MIN_MINING_TARGET_PERCENTAGE = 5; + static constexpr uint8_t BACKGROUND_MINING_MAX_MINING_TARGET_PERCENTAGE = 50; + static constexpr uint8_t BACKGROUND_MINING_MINER_MONITOR_INVERVAL_IN_SECONDS = 10; + static constexpr uint64_t BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS = 400; // ramp up + static constexpr uint64_t BACKGROUND_MINING_MIN_MINER_EXTRA_SLEEP_MILLIS = 5; private: bool worker_thread(); @@ -119,8 +148,26 @@ namespace cryptonote bool m_do_print_hashrate; bool m_do_mining; + // background mining stuffs .. + + bool set_is_background_mining_enabled(bool is_background_mining_enabled); + void set_ignore_battery(bool ignore_battery); + bool background_worker_thread(); + std::atomic<bool> m_is_background_mining_enabled; + bool m_ignore_battery; + boost::mutex m_is_background_mining_enabled_mutex; + boost::condition_variable m_is_background_mining_enabled_cond; + std::atomic<bool> m_is_background_mining_started; + boost::mutex m_is_background_mining_started_mutex; + boost::condition_variable m_is_background_mining_started_cond; + boost::thread m_background_mining_thread; + uint64_t m_min_idle_seconds; + uint8_t m_idle_threshold; + uint8_t m_mining_target; + std::atomic<uint64_t> m_miner_extra_sleep; + static bool get_system_times(uint64_t& total_time, uint64_t& idle_time); + static bool get_process_time(uint64_t& total_time); + static uint8_t get_percent_of_total(uint64_t some_time, uint64_t total_time); + static boost::logic::tribool on_battery_power(); }; } - - - diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_basic/tx_extra.h index 6f5fbe466..5a6c3176d 100644 --- a/src/cryptonote_core/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_basic/verification_context.h index 0bb635e84..ce885ec1d 100644 --- a/src/cryptonote_core/verification_context.h +++ b/src/cryptonote_basic/verification_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 3f15cee3f..8dbf1b53b 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -57,6 +57,7 @@ #define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 60000 //size of block (bytes) after which reward for block calculated using block size #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 20000 //size of block (bytes) after which reward for block calculated using block size - before first fork +#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 300000 //size of block (bytes) after which reward for block calculated using block size - second change, from v5 #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 #define CRYPTONOTE_DISPLAY_DECIMAL_POINT 12 // COIN - number of smallest units in one coin @@ -66,6 +67,7 @@ #define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9) #define DYNAMIC_FEE_PER_KB_BASE_FEE ((uint64_t)2000000000) // 2 * pow(10,9) #define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000000) // 10 * pow(10,12) +#define DYNAMIC_FEE_PER_KB_BASE_FEE_V5 ((uint64_t)2000000000 * (uint64_t)CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5) #define ORPHANED_BLOCKS_MAX_COUNT 100 @@ -129,6 +131,10 @@ #define THREAD_STACK_SIZE 5 * 1024 * 1024 #define HF_VERSION_DYNAMIC_FEE 4 +#define HF_VERSION_MIN_MIXIN_4 6 +#define HF_VERSION_ENFORCE_RCT 6 + +#define PER_KB_FEE_QUANTIZATION_DECIMALS 8 // New constants are intended to go here namespace config diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index ad1745b7f..5944ddcd1 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -27,38 +27,19 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(cryptonote_core_sources - account.cpp blockchain.cpp - checkpoints.cpp - cryptonote_basic_impl.cpp cryptonote_core.cpp - cryptonote_format_utils.cpp - difficulty.cpp - miner.cpp tx_pool.cpp - hardfork.cpp) + cryptonote_tx_utils.cpp) set(cryptonote_core_headers) set(cryptonote_core_private_headers - account.h - account_boost_serialization.h blockchain_storage_boost_serialization.h blockchain.h - checkpoints.h - connection_context.h - cryptonote_basic.h - cryptonote_basic_impl.h - cryptonote_boost_serialization.h cryptonote_core.h - cryptonote_format_utils.h - cryptonote_stat_info.h - difficulty.h - miner.h - tx_extra.h tx_pool.h - verification_context.h - hardfork.h) + cryptonote_tx_utils.h) if(PER_BLOCK_CHECKPOINT) set(Blocks "blocks") diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index b344c5597..74c5b3b83 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,14 +34,13 @@ #include <boost/range/adaptor/reversed.hpp> #include "include_base_utils.h" -#include "cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "tx_pool.h" #include "blockchain.h" #include "blockchain_db/blockchain_db.h" -#include "cryptonote_format_utils.h" -#include "cryptonote_boost_serialization.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_config.h" -#include "miner.h" +#include "cryptonote_basic/miner.h" #include "misc_language.h" #include "profile_tools.h" #include "file_io_utils.h" @@ -49,8 +48,8 @@ #include "common/boost_serialization_helper.h" #include "warnings.h" #include "crypto/hash.h" -#include "cryptonote_core/checkpoints.h" -#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_basic/checkpoints.h" +#include "cryptonote_core.h" #include "ringct/rctSigs.h" #include "common/perf_timer.h" #if defined(PER_BLOCK_CHECKPOINT) @@ -98,8 +97,8 @@ static const struct { // version 4 starts from block 1220516, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18. { 4, 1220516, 0, 1483574400 }, - // version 5 starts from block 1406997, which is on or around the 20th of September, 2017. Fork time finalised on 2016-09-18. - { 5, 1406997, 0, 1505865600 }, + // version 5 starts from block 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14. + { 5, 1288616, 0, 1489520158 }, }; static const uint64_t mainnet_hard_fork_version_1_till = 1009826; @@ -118,7 +117,7 @@ static const struct { // versions 3-5 were passed in rapid succession from September 18th, 2016 { 3, 800500, 0, 1472415034 }, { 4, 801219, 0, 1472415035 }, - { 5, 802660, 0, 1472415036 }, + { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6 }; static const uint64_t testnet_hard_fork_version_1_till = 624633; @@ -1073,7 +1072,7 @@ uint64_t Blockchain::get_current_cumulative_blocksize_limit() const // in a lot of places. That flag is not referenced in any of the code // nor any of the makefiles, howeve. Need to look into whether or not it's // necessary at all. -bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) +bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) { LOG_PRINT_L3("Blockchain::" << __func__); size_t median_size; @@ -1097,7 +1096,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m size_t txs_size; uint64_t fee; - if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, m_hardfork->get_current_version())) + if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, expected_reward, m_hardfork->get_current_version())) { return false; } @@ -1432,7 +1431,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id return true; } //------------------------------------------------------------------ -bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const +bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1444,17 +1443,17 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block return false; } - for(const block& blk : blocks) + for(const auto& blk : blocks) { std::list<crypto::hash> missed_ids; - get_transactions(blk.tx_hashes, txs, missed_ids); + get_transactions_blobs(blk.second.tx_hashes, txs, missed_ids); CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain"); } return true; } //------------------------------------------------------------------ -bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const +bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1463,7 +1462,12 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block for(size_t i = start_offset; i < start_offset + count && i < m_db->height();i++) { - blocks.push_back(m_db->get_block_from_height(i)); + blocks.push_back(std::make_pair(m_db->get_block_blob_from_height(i), block())); + if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second)) + { + LOG_ERROR("Invalid block"); + return false; + } } return true; } @@ -1481,22 +1485,22 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO CRITICAL_REGION_LOCAL(m_blockchain_lock); m_db->block_txn_start(true); rsp.current_blockchain_height = get_current_blockchain_height(); - std::list<block> blocks; + std::list<std::pair<cryptonote::blobdata,block>> blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); for (const auto& bl: blocks) { std::list<crypto::hash> missed_tx_ids; - std::list<transaction> txs; + std::list<cryptonote::blobdata> txs; // FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids // is for missed blocks, not missed transactions as well. - get_transactions(bl.tx_hashes, txs, missed_tx_ids); + get_transactions_blobs(bl.second.tx_hashes, txs, missed_tx_ids); if (missed_tx_ids.size() != 0) { LOG_ERROR("Error retrieving blocks, missed " << missed_tx_ids.size() - << " transactions for block with hash: " << get_block_hash(bl) + << " transactions for block with hash: " << get_block_hash(bl.second) << std::endl ); @@ -1511,17 +1515,17 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO rsp.blocks.push_back(block_complete_entry()); block_complete_entry& e = rsp.blocks.back(); //pack block - e.block = t_serializable_object_to_blob(bl); + e.block = bl.first; //pack transactions - for (transaction& tx: txs) - e.txs.push_back(t_serializable_object_to_blob(tx)); + for (const cryptonote::blobdata& tx: txs) + e.txs.push_back(tx); } //get another transactions, if need - std::list<transaction> txs; - get_transactions(arg.txs, txs, rsp.missed_ids); + std::list<cryptonote::blobdata> txs; + get_transactions_blobs(arg.txs, txs, rsp.missed_ids); //pack aside transactions for (const auto& tx: txs) - rsp.txs.push_back(t_serializable_object_to_blob(tx)); + rsp.txs.push_back(tx); m_db->block_txn_stop(); return true; @@ -1890,7 +1894,12 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container { try { - blocks.push_back(m_db->get_block(block_hash)); + blocks.push_back(std::make_pair(m_db->get_block_blob(block_hash), block())); + if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second)) + { + LOG_ERROR("Invalid block"); + return false; + } } catch (const BLOCK_DNE& e) { @@ -1907,7 +1916,7 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container //TODO: return type should be void, throw on exception // alternatively, return true only if no transactions missed template<class t_ids_container, class t_tx_container, class t_missed_container> -bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const +bool Blockchain::get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1916,8 +1925,8 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container { try { - transaction tx; - if (m_db->get_tx(tx_hash, tx)) + cryptonote::blobdata tx; + if (m_db->get_tx_blob(tx_hash, tx)) txs.push_back(std::move(tx)); else missed_txs.push_back(tx_hash); @@ -1930,6 +1939,37 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container return true; } //------------------------------------------------------------------ +template<class t_ids_container, class t_tx_container, class t_missed_container> +bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + for (const auto& tx_hash : txs_ids) + { + try + { + cryptonote::blobdata tx; + if (m_db->get_tx_blob(tx_hash, tx)) + { + txs.push_back(transaction()); + if (!parse_and_validate_tx_from_blob(tx, txs.back())) + { + LOG_ERROR("Invalid transaction"); + return false; + } + } + else + missed_txs.push_back(tx_hash); + } + catch (const std::exception& e) + { + return false; + } + } + return true; +} +//------------------------------------------------------------------ void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) const { LOG_PRINT_L3("Blockchain::" << __func__); @@ -1987,12 +2027,14 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc return false; } + m_db->block_txn_start(true); resp.total_height = get_current_blockchain_height(); size_t count = 0; for(size_t i = resp.start_height; i < resp.total_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) { resp.m_block_ids.push_back(m_db->get_block_hash_from_height(i)); } + m_db->block_txn_stop(); return true; } //------------------------------------------------------------------ @@ -2000,7 +2042,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc // find split point between ours and foreign blockchain (or start at // blockchain height <req_start_block>), and return up to max_count FULL // blocks by reference. -bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const +bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2023,16 +2065,20 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons } } + m_db->block_txn_start(true); total_height = get_current_blockchain_height(); size_t count = 0; for(size_t i = start_height; i < total_height && count < max_count; i++, count++) { blocks.resize(blocks.size()+1); - blocks.back().first = m_db->get_block_from_height(i); + blocks.back().first = m_db->get_block_blob_from_height(i); + block b; + CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first, b), false, "internal error, invalid block"); std::list<crypto::hash> mis; - get_transactions(blocks.back().first.tx_hashes, blocks.back().second, mis); + get_transactions_blobs(b.tx_hashes, blocks.back().second, mis); CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); } + m_db->block_txn_stop(); return true; } //------------------------------------------------------------------ @@ -2380,7 +2426,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { size_t n_unmixable = 0, n_mixable = 0; size_t mixin = std::numeric_limits<size_t>::max(); - const size_t min_mixin = hf_version >= 5 ? 4 : 2; + const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; for (const auto& txin : tx.vin) { // non txin_to_key inputs will be rejected below @@ -2433,7 +2479,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, tvc.m_verifivation_failed = true; return false; } - const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= 5) ? 2 : 1); + const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= HF_VERSION_ENFORCE_RCT) ? 2 : 1); if (tx.version < min_tx_version) { MERROR_VER("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version); @@ -2750,21 +2796,43 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const } //------------------------------------------------------------------ -uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size) +static uint64_t get_fee_quantization_mask() { - if (median_block_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2) - median_block_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + static uint64_t mask = 0; + if (mask == 0) + { + mask = 1; + for (size_t n = PER_KB_FEE_QUANTIZATION_DECIMALS; n < CRYPTONOTE_DISPLAY_DECIMAL_POINT; ++n) + mask *= 10; + } + return mask; +} + +//------------------------------------------------------------------ +uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version) +{ + const uint64_t min_block_size = get_min_block_size(version); + const uint64_t fee_per_kb_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE; + + if (median_block_size < min_block_size) + median_block_size = min_block_size; - uint64_t unscaled_fee_per_kb = (DYNAMIC_FEE_PER_KB_BASE_FEE * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / median_block_size); + uint64_t unscaled_fee_per_kb = (fee_per_kb_base * min_block_size / median_block_size); uint64_t hi, lo = mul128(unscaled_fee_per_kb, block_reward, &hi); static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000"); static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits<uint32_t>::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large"); + // divide in two steps, since the divisor must be 32 bits, but DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD isn't div128_32(hi, lo, DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000, &hi, &lo); div128_32(hi, lo, 1000000, &hi, &lo); assert(hi == 0); - return lo; + // quantize fee up to 8 decimals + uint64_t mask = get_fee_quantization_mask(); + uint64_t qlo = (lo + mask - 1) / mask * mask; + MDEBUG("lo " << print_money(lo) << ", qlo " << print_money(qlo) << ", mask " << mask); + + return qlo; } //------------------------------------------------------------------ @@ -2784,7 +2852,7 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const uint64_t base_reward; if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) return false; - fee_per_kb = get_dynamic_per_kb_fee(base_reward, median); + fee_per_kb = get_dynamic_per_kb_fee(base_reward, median, version); } MDEBUG("Using " << print_money(fee) << "/kB fee"); @@ -2792,7 +2860,7 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const needed_fee += (blob_size % 1024) ? 1 : 0; needed_fee *= fee_per_kb; - if (fee < needed_fee) + if (fee < needed_fee * 0.98) // keep a little buffer on acceptance { MERROR_VER("transaction fee is not enough: " << print_money(fee) << ", minimum fee: " << print_money(needed_fee)); return false; @@ -2811,14 +2879,15 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW) grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1; + const uint64_t min_block_size = get_min_block_size(version); std::vector<size_t> sz; get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); for (size_t i = 0; i < grace_blocks; ++i) - sz.push_back(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2); + sz.push_back(min_block_size); uint64_t median = epee::misc_utils::median(sz); - if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2) - median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + if(median <= min_block_size) + median = min_block_size; uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; uint64_t base_reward; @@ -2828,7 +2897,7 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons base_reward = BLOCK_REWARD_OVERESTIMATE; } - uint64_t fee = get_dynamic_per_kb_fee(base_reward, median); + uint64_t fee = get_dynamic_per_kb_fee(base_reward, median, version); MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/kB"); return fee; } @@ -3347,7 +3416,7 @@ leave: //------------------------------------------------------------------ bool Blockchain::update_next_cumulative_size_limit() { - uint64_t full_reward_zone = get_current_hard_fork_version() < 2 ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; + uint64_t full_reward_zone = get_min_block_size(get_current_hard_fork_version()); LOG_PRINT_L3("Blockchain::" << __func__); std::vector<size_t> sz; @@ -3541,7 +3610,7 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uin { try { - m_db->get_output_key(amount, offsets, outputs); + m_db->get_output_key(amount, offsets, outputs, true); } catch (const std::exception& e) { @@ -3596,6 +3665,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e std::vector<std::unordered_map<crypto::hash, crypto::hash>> maps(threads); std::vector < std::vector < block >> blocks(threads); auto it = blocks_entry.begin(); + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); for (uint64_t i = 0; i < threads; i++) { @@ -3655,7 +3726,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e m_blocks_longhash_table.clear(); for (uint64_t i = 0; i < threads; i++) { - thread_list.push_back(new boost::thread(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i]))); + thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i])))); } for (size_t j = 0; j < thread_list.size(); j++) @@ -3949,10 +4020,37 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) +static const char expected_block_hashes_hash[] = "23d8a8c73de7b2383c72a016d9a6034e69d62dd48077d1c414e064ceab6daa94"; void Blockchain::load_compiled_in_block_hashes() { - if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr) + if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr && get_blocks_dat_size(m_testnet) > 0) { + MINFO("Loading precomputed blocks (" << get_blocks_dat_size(m_testnet) << " bytes)"); + + if (!m_testnet) + { + // first check hash + crypto::hash hash; + if (!tools::sha256sum(get_blocks_dat_start(m_testnet), get_blocks_dat_size(m_testnet), hash)) + { + MERROR("Failed to hash precomputed blocks data"); + return; + } + MINFO("precomputed blocks hash: " << hash << ", expected " << expected_block_hashes_hash); + cryptonote::blobdata expected_hash_data; + if (!epee::string_tools::parse_hexstr_to_binbuff(std::string(expected_block_hashes_hash), expected_hash_data) || expected_hash_data.size() != sizeof(crypto::hash)) + { + MERROR("Failed to parse expected block hashes hash"); + return; + } + const crypto::hash expected_hash = *reinterpret_cast<const crypto::hash*>(expected_hash_data.data()); + if (hash != expected_hash) + { + MERROR("Block hash data does not match expected hash"); + return; + } + } + if (get_blocks_dat_size(m_testnet) > 4) { const unsigned char *p = get_blocks_dat_start(m_testnet); @@ -3960,7 +4058,6 @@ void Blockchain::load_compiled_in_block_hashes() const size_t size_needed = 4 + nblocks * sizeof(crypto::hash); if(nblocks > 0 && nblocks > m_db->height() && get_blocks_dat_size(m_testnet) >= size_needed) { - MINFO("Loading precomputed blocks: " << nblocks); p += sizeof(uint32_t); for (uint32_t i = 0; i < nblocks; i++) { @@ -3969,6 +4066,7 @@ void Blockchain::load_compiled_in_block_hashes() p += sizeof(hash.data); m_blocks_hash_check.push_back(hash); } + MINFO(nblocks << " block hashes loaded"); // FIXME: clear tx_pool because the process might have been // terminated and caused it to store txs kept by blocks. @@ -4012,3 +4110,7 @@ bool Blockchain::for_all_outputs(std::function<bool(uint64_t amount, const crypt { return m_db->for_all_outputs(f);; } + +namespace cryptonote { +template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::list<transaction>&, std::list<crypto::hash>&) const; +} diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index cd452faf4..46f7ac682 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,6 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once +#include <boost/asio/io_service.hpp> #include <boost/serialization/serialization.hpp> #include <boost/serialization/version.hpp> #include <boost/serialization/list.hpp> @@ -42,16 +43,16 @@ #include "syncobj.h" #include "string_tools.h" -#include "cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "common/util.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "difficulty.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "verification_context.h" +#include "cryptonote_basic/difficulty.h" +#include "cryptonote_tx_utils.h" +#include "cryptonote_basic/verification_context.h" #include "crypto/hash.h" -#include "checkpoints.h" -#include "hardfork.h" +#include "cryptonote_basic/checkpoints.h" +#include "cryptonote_basic/hardfork.h" #include "blockchain_db/blockchain_db.h" namespace cryptonote @@ -153,7 +154,7 @@ namespace cryptonote * * @return false if start_offset > blockchain height, else true */ - bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const; + bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const; /** * @brief get blocks from blocks based on start height and count @@ -164,7 +165,7 @@ namespace cryptonote * * @return false if start_offset > blockchain height, else true */ - bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const; + bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const; /** * @brief compiles a list of all blocks stored as alternative chains @@ -322,11 +323,12 @@ namespace cryptonote * @param miner_address address new coins for the block will go to * @param di return-by-reference tells the miner what the difficulty target is * @param height return-by-reference tells the miner what height it's mining against + * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param ex_nonce extra data to be added to the miner transaction's extra * * @return true if block template filled in successfully, else false */ - bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce); + bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce); /** * @brief checks if a block is known about with a given hash @@ -407,7 +409,7 @@ namespace cryptonote * * @return true if a block found in common or req_start_block specified, else false */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const; /** * @brief retrieves a set of blocks and their transactions, and possibly other transactions @@ -520,10 +522,11 @@ namespace cryptonote * * @param block_reward the current block reward * @param median_block_size the median blob's size in the past window + * @param version hard fork version for rules and constants to use * * @return the per kB fee */ - static uint64_t get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size); + static uint64_t get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version); /** * @brief get dynamic per kB fee estimate for the next few blocks @@ -621,9 +624,10 @@ namespace cryptonote * @return false if an unexpected exception occurs, else true */ template<class t_ids_container, class t_tx_container, class t_missed_container> + bool get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const; + template<class t_ids_container, class t_tx_container, class t_missed_container> bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const; - //debug functions /** diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h index 0518b9aa7..e87a51f10 100644 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 900dc58ba..9c122f511 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -35,14 +35,16 @@ using namespace epee; #include "cryptonote_core.h" #include "common/command_line.h" #include "common/util.h" +#include "common/updates.h" +#include "common/download.h" #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" -#include "cryptonote_format_utils.h" +#include "cryptonote_tx_utils.h" #include "misc_language.h" #include <csignal> #include <p2p/net_node.h> -#include "cryptonote_core/checkpoints.h" +#include "cryptonote_basic/checkpoints.h" #include "ringct/rctTypes.h" #include "blockchain_db/blockchain_db.h" #include "blockchain_db/lmdb/db_lmdb.h" @@ -58,6 +60,8 @@ DISABLE_VS_WARNINGS(4355) #define MERROR_VER(x) MCERROR("verify", x) +#define BAD_SEMANTICS_TXES_MAX_SIZE 100 + namespace cryptonote { @@ -71,7 +75,8 @@ namespace cryptonote m_target_blockchain_height(0), m_checkpoints_path(""), m_last_dns_checkpoints_update(0), - m_last_json_checkpoints_update(0) + m_last_json_checkpoints_update(0), + m_update_download(0) { set_cryptonote_protocol(pprotocol); } @@ -130,6 +135,15 @@ namespace cryptonote void core::stop() { m_blockchain_storage.cancel(); + + tools::download_async_handle handle; + { + boost::lock_guard<boost::mutex> lock(m_update_mutex); + handle = m_update_download; + m_update_download = 0; + } + if (handle) + tools::download_cancel(handle); } //----------------------------------------------------------------------------------- void core::init_options(boost::program_options::options_description& desc) @@ -148,6 +162,13 @@ namespace cryptonote command_line::add_arg(desc, command_line::arg_db_sync_mode); command_line::add_arg(desc, command_line::arg_show_time_stats); command_line::add_arg(desc, command_line::arg_block_sync_size); + command_line::add_arg(desc, command_line::arg_check_updates); + + // we now also need some of net_node's options (p2p bind arg, for separate data dir) + command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false); + command_line::add_arg(desc, nodetool::arg_p2p_bind_port, false); + + miner::init_options(desc); } //----------------------------------------------------------------------------------------------- bool core::handle_command_line(const boost::program_options::variables_map& vm) @@ -195,62 +216,44 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const + bool core::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs); } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const + bool core::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const { return m_blockchain_storage.get_blocks(start_offset, count, blocks); - } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const + } + //----------------------------------------------------------------------------------------------- + bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const { - return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); + std::list<std::pair<cryptonote::blobdata, cryptonote::block>> bs; + if (!m_blockchain_storage.get_blocks(start_offset, count, bs)) + return false; + for (const auto &b: bs) + blocks.push_back(b.second); + return true; } //----------------------------------------------------------------------------------------------- - bool core::get_alternative_blocks(std::list<block>& blocks) const + bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<cryptonote::blobdata>& txs, std::list<crypto::hash>& missed_txs) const { - return m_blockchain_storage.get_alternative_blocks(blocks); + return m_blockchain_storage.get_transactions_blobs(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - size_t core::get_alternative_blocks_count() const + bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const { - return m_blockchain_storage.get_alternative_blocks_count(); + return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - bool core::lock_db_directory(const boost::filesystem::path &path) + bool core::get_alternative_blocks(std::list<block>& blocks) const { - // boost doesn't like locking directories... - const boost::filesystem::path lock_path = path / ".daemon_lock"; - - try - { - // ensure the file exists - std::ofstream(lock_path.string(), std::ios::out).close(); - - db_lock = boost::interprocess::file_lock(lock_path.string().c_str()); - LOG_PRINT_L1("Locking " << lock_path.string()); - if (!db_lock.try_lock()) - { - LOG_ERROR("Failed to lock " << lock_path.string()); - return false; - } - return true; - } - catch (const std::exception &e) - { - LOG_ERROR("Error trying to lock " << lock_path.string() << ": " << e.what()); - return false; - } + return m_blockchain_storage.get_alternative_blocks(blocks); } //----------------------------------------------------------------------------------------------- - bool core::unlock_db_directory() + size_t core::get_alternative_blocks_count() const { - db_lock.unlock(); - db_lock = boost::interprocess::file_lock(); - LOG_PRINT_L1("Blockchain directory successfully unlocked"); - return true; + return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- bool core::init(const boost::program_options::variables_map& vm, const cryptonote::test_options *test_options) @@ -276,6 +279,7 @@ namespace cryptonote std::string db_sync_mode = command_line::get_arg(vm, command_line::arg_db_sync_mode); bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0; uint64_t blocks_threads = command_line::get_arg(vm, command_line::arg_prep_blocks_threads); + std::string check_updates_string = command_line::get_arg(vm, command_line::arg_check_updates); boost::filesystem::path folder(m_config_folder); if (m_fakechain) @@ -284,11 +288,6 @@ namespace cryptonote // make sure the data directory exists, and try to lock it CHECK_AND_ASSERT_MES (boost::filesystem::exists(folder) || boost::filesystem::create_directories(folder), false, std::string("Failed to create directory ").append(folder.string()).c_str()); - if (!lock_db_directory (folder)) - { - LOG_ERROR ("Failed to lock " << folder); - return false; - } // check for blockchain.bin try @@ -418,6 +417,20 @@ namespace cryptonote // with respect to what blocks we already have CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); + // DNS versions checking + if (check_updates_string == "disabled") + check_updates_level = UPDATES_DISABLED; + else if (check_updates_string == "notify") + check_updates_level = UPDATES_NOTIFY; + else if (check_updates_string == "download") + check_updates_level = UPDATES_DOWNLOAD; + else if (check_updates_string == "update") + check_updates_level = UPDATES_UPDATE; + else { + MERROR("Invalid argument to --dns-versions-check: " << check_updates_string); + return false; + } + r = m_miner.init(vm, m_testnet); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner instance"); @@ -440,7 +453,6 @@ namespace cryptonote m_miner.stop(); m_mempool.deinit(); m_blockchain_storage.deinit(); - unlock_db_directory(); return true; } //----------------------------------------------------------------------------------------------- @@ -496,11 +508,14 @@ namespace cryptonote } //std::cout << "!"<< tx.vin.size() << std::endl; - if (bad_semantics_txes.find(tx_hash) != bad_semantics_txes.end()) + for (int idx = 0; idx < 2; ++idx) { - LOG_PRINT_L1("Transaction already seen with bad semantics, rejected"); - tvc.m_verifivation_failed = true; - return false; + if (bad_semantics_txes[idx].find(tx_hash) != bad_semantics_txes[idx].end()) + { + LOG_PRINT_L1("Transaction already seen with bad semantics, rejected"); + tvc.m_verifivation_failed = true; + return false; + } } uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); @@ -551,8 +566,13 @@ namespace cryptonote if(!check_tx_semantic(tx, keeped_by_block)) { LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); - bad_semantics_txes.insert(tx_hash); tvc.m_verifivation_failed = true; + bad_semantics_txes[0].insert(tx_hash); + if (bad_semantics_txes[0].size() >= BAD_SEMANTICS_TXES_MAX_SIZE) + { + std::swap(bad_semantics_txes[0], bad_semantics_txes[1]); + bad_semantics_txes[0].clear(); + } return false; } @@ -639,6 +659,12 @@ namespace cryptonote return false; } + if (!check_tx_inputs_keyimages_domain(tx)) + { + MERROR_VER("tx uses key image not in the valid domain"); + return false; + } + if (tx.version >= 2) { const rct::rctSig &rv = tx.rct_signatures; @@ -722,6 +748,18 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::check_tx_inputs_keyimages_domain(const transaction& tx) const + { + std::unordered_set<crypto::key_image> ki; + for(const auto& in: tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + if (!(rct::scalarmultKey(rct::ki2rct(tokey_in.k_image), rct::curveOrder()) == rct::identity())) + return false; + } + return true; + } + //----------------------------------------------------------------------------------------------- bool core::add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { crypto::hash tx_hash = get_transaction_hash(tx); @@ -758,7 +796,7 @@ namespace cryptonote { // we attempt to relay txes that should be relayed, but were not std::list<std::pair<crypto::hash, cryptonote::transaction>> txs; - if (m_mempool.get_relayable_transactions(txs)) + if (m_mempool.get_relayable_transactions(txs) && !txs.empty()) { cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); tx_verification_context tvc = AUTO_VAL_INIT(tvc); @@ -789,9 +827,9 @@ namespace cryptonote m_mempool.set_relayed(txs); } //----------------------------------------------------------------------------------------------- - bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) + bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) { - return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); + return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce); } //----------------------------------------------------------------------------------------------- bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const @@ -799,7 +837,7 @@ namespace cryptonote return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); } //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const + bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const { return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, max_count); } @@ -849,11 +887,28 @@ namespace cryptonote m_miner.resume(); } //----------------------------------------------------------------------------------------------- + block_complete_entry get_block_complete_entry(block& b, tx_memory_pool &pool) + { + block_complete_entry bce; + bce.block = cryptonote::block_to_blob(b); + for (const auto &tx_hash: b.tx_hashes) + { + cryptonote::transaction tx; + CHECK_AND_ASSERT_THROW_MES(pool.get_transaction(tx_hash, tx), "Transaction not found in pool"); + bce.txs.push_back(cryptonote::tx_to_blob(tx)); + } + return bce; + } + //----------------------------------------------------------------------------------------------- bool core::handle_block_found(block& b) { block_verification_context bvc = boost::value_initialized<block_verification_context>(); m_miner.pause(); + std::list<block_complete_entry> blocks; + blocks.push_back(get_block_complete_entry(b, m_mempool)); + prepare_handle_incoming_blocks(blocks); m_blockchain_storage.add_new_block(b, bvc); + cleanup_handle_incoming_blocks(true); //anyway - update miner template update_miner_block_template(); m_miner.resume(); @@ -867,8 +922,8 @@ namespace cryptonote arg.hop = 0; arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); std::list<crypto::hash> missed_txs; - std::list<transaction> txs; - m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs); + std::list<cryptonote::blobdata> txs; + m_blockchain_storage.get_transactions_blobs(b.tx_hashes, txs, missed_txs); if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { LOG_PRINT_L1("Block found but, seems that reorganize just happened after that, do not relay this block"); @@ -880,7 +935,7 @@ namespace cryptonote block_to_blob(b, arg.b.block); //pack transactions for(auto& tx: txs) - arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + arg.b.txs.push_back(tx); m_pprotocol->relay_block(arg, exclude_context); } @@ -981,7 +1036,13 @@ namespace cryptonote m_mempool.get_transactions(txs); return true; } - //----------------------------------------------------------------------------------------------- + //----------------------------------------------------------------------------------------------- + bool core::get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const + { + m_mempool.get_transaction_hashes(txs); + return true; + } + //----------------------------------------------------------------------------------------------- bool core::get_pool_transaction(const crypto::hash &id, transaction& tx) const { return m_mempool.get_transaction(id, tx); @@ -1040,6 +1101,7 @@ namespace cryptonote m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this)); m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this)); + m_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_miner.on_idle(); m_mempool.on_idle(); return true; @@ -1067,6 +1129,99 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::check_updates() + { + static const char software[] = "monero"; + static const char subdir[] = "cli"; // because it can never be simple +#ifdef BUILD_TAG + static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); +#else + static const char buildtag[] = "source"; +#endif + + if (check_updates_level == UPDATES_DISABLED) + return true; + + std::string version, hash; + MCDEBUG("updates", "Checking for a new " << software << " version for " << buildtag); + if (!tools::check_updates(software, buildtag, version, hash)) + return false; + + if (tools::vercmp(version.c_str(), MONERO_VERSION) <= 0) + return true; + + std::string url = tools::get_update_url(software, subdir, buildtag, version, true); + MCLOG_CYAN(el::Level::Info, "global", "Version " << version << " of " << software << " for " << buildtag << " is available: " << url << ", SHA256 hash " << hash); + + if (check_updates_level == UPDATES_NOTIFY) + return true; + + url = tools::get_update_url(software, subdir, buildtag, version, false); + std::string filename; + const char *slash = strrchr(url.c_str(), '/'); + if (slash) + filename = slash + 1; + else + filename = std::string(software) + "-update-" + version; + boost::filesystem::path path(epee::string_tools::get_current_module_folder()); + path /= filename; + + boost::unique_lock<boost::mutex> lock(m_update_mutex); + + if (m_update_download != 0) + { + MCDEBUG("updates", "Already downloading update"); + return true; + } + + crypto::hash file_hash; + if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash))) + { + MCDEBUG("updates", "We don't have that file already, downloading"); + m_last_update_length = 0; + m_update_download = tools::download_async(path.string(), url, [this, hash](const std::string &path, const std::string &uri, bool success) { + if (success) + { + crypto::hash file_hash; + if (!tools::sha256sum(path, file_hash)) + { + MCERROR("updates", "Failed to hash " << path); + } + if (hash != epee::string_tools::pod_to_hex(file_hash)) + { + MCERROR("updates", "Download from " << uri << " does not match the expected hash"); + } + MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path); + } + else + { + MCERROR("updates", "Failed to download " << uri); + } + boost::unique_lock<boost::mutex> lock(m_update_mutex); + m_update_download = 0; + }, [this](const std::string &path, const std::string &uri, size_t length, ssize_t content_length) { + if (length >= m_last_update_length + 1024 * 1024 * 10) + { + m_last_update_length = length; + MCDEBUG("updates", "Downloaded " << length << "/" << (content_length ? std::to_string(content_length) : "unknown")); + } + return true; + }); + } + else + { + MCDEBUG("updates", "We already have " << path << " with expected hash"); + } + + lock.unlock(); + + if (check_updates_level == UPDATES_DOWNLOAD) + return true; + + MCERROR("updates", "Download/update not implemented yet"); + return true; + } + //----------------------------------------------------------------------------------------------- void core::set_target_blockchain_height(uint64_t target_blockchain_height) { m_target_blockchain_height = target_blockchain_height; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 1b9518c96..e56c2dcf1 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -39,11 +39,12 @@ #include "p2p/net_node_common.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "storages/portable_storage_template_helper.h" +#include "common/download.h" #include "tx_pool.h" #include "blockchain.h" -#include "miner.h" -#include "connection_context.h" -#include "cryptonote_core/cryptonote_stat_info.h" +#include "cryptonote_basic/miner.h" +#include "cryptonote_basic/connection_context.h" +#include "cryptonote_basic/cryptonote_stat_info.h" #include "warnings.h" #include "crypto/hash.h" @@ -179,7 +180,7 @@ namespace cryptonote * * @note see Blockchain::create_block_template */ - virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce); + virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce); /** * @brief called when a transaction is relayed @@ -287,16 +288,23 @@ namespace cryptonote bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) const; /** - * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<block>&, std::list<transaction>&) const + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&, std::list<transaction>&) const * - * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<block>&, std::list<transaction>&) const + * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&, std::list<transaction>&) const */ - bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const; + bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const; /** - * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<block>&) const + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const * - * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<block>&) const + * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const + */ + bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const; + + /** + * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const + * + * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const */ bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const; @@ -323,6 +331,13 @@ namespace cryptonote * * @note see Blockchain::get_transactions */ + bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<cryptonote::blobdata>& txs, std::list<crypto::hash>& missed_txs) const; + + /** + * @copydoc Blockchain::get_transactions + * + * @note see Blockchain::get_transactions + */ bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const; /** @@ -382,6 +397,13 @@ namespace cryptonote bool get_pool_transactions(std::list<transaction>& txs) const; /** + * @copydoc tx_memory_pool::get_transactions + * + * @note see tx_memory_pool::get_transactions + */ + bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const; + + /** * @copydoc tx_memory_pool::get_transaction * * @note see tx_memory_pool::get_transaction @@ -431,11 +453,11 @@ namespace cryptonote bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const; /** - * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<block, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const + * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >&, uint64_t&, uint64_t&, size_t) const * - * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<block, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const + * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<cryptonote::blobdata, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const; /** * @brief gets some stats about the daemon @@ -745,6 +767,16 @@ namespace cryptonote bool check_tx_inputs_keyimages_diff(const transaction& tx) const; /** + * @brief verify that each input key image in a transaction is in + * the valid domain + * + * @param tx the transaction to check + * + * @return false if any key image is not in the valid domain, otherwise true + */ + bool check_tx_inputs_keyimages_domain(const transaction& tx) const; + + /** * @brief checks HardFork status and prints messages about it * * Checks the status of HardFork and logs/prints if an update to @@ -764,22 +796,11 @@ namespace cryptonote bool relay_txpool_transactions(); /** - * @brief locks a file in the BlockchainDB directory - * - * @param path the directory in which to place the file - * - * @return true if lock acquired successfully, otherwise false - */ - bool lock_db_directory(const boost::filesystem::path &path); - - /** - * @brief unlocks the db directory - * - * @note see lock_db_directory() + * @brief checks DNS versions * - * @return true + * @return true on success, false otherwise */ - bool unlock_db_directory(); + bool check_updates(); bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing) @@ -801,10 +822,10 @@ namespace cryptonote cryptonote_protocol_stub m_protocol_stub; //!< cryptonote protocol stub instance epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; //!< interval for manual storing of Blockchain, if enabled - epee::math_helper::once_a_time_seconds<60*60*2, false> m_fork_moaner; //!< interval for checking HardFork status + epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner; //!< interval for checking HardFork status epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions + epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions - friend class tx_validate_inputs; std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown? uint64_t m_target_blockchain_height; //!< blockchain height target @@ -819,13 +840,22 @@ namespace cryptonote std::atomic_flag m_checkpoints_updating; //!< set if checkpoints are currently updating to avoid multiple threads attempting to update at once - boost::interprocess::file_lock db_lock; //!< a lock object for a file lock in the db directory - size_t block_sync_size; time_t start_time; - std::unordered_set<crypto::hash> bad_semantics_txes; + std::unordered_set<crypto::hash> bad_semantics_txes[2]; + + enum { + UPDATES_DISABLED, + UPDATES_NOTIFY, + UPDATES_DOWNLOAD, + UPDATES_UPDATE, + } check_updates_level; + + tools::download_async_handle m_update_download; + size_t m_last_update_length; + boost::mutex m_update_mutex; }; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp new file mode 100644 index 000000000..26d5fb767 --- /dev/null +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -0,0 +1,499 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "include_base_utils.h" +using namespace epee; + +#include "cryptonote_tx_utils.h" +#include "cryptonote_config.h" +#include "cryptonote_basic/miner.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" +#include "ringct/rctSigs.h" + +namespace cryptonote +{ + //--------------------------------------------------------------- + bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { + tx.vin.clear(); + tx.vout.clear(); + tx.extra.clear(); + + keypair txkey = keypair::generate(); + add_tx_pub_key_to_extra(tx, txkey.pub); + if(!extra_nonce.empty()) + if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) + return false; + + txin_gen in; + in.height = height; + + uint64_t block_reward; + if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version)) + { + LOG_PRINT_L0("Block is too big"); + return false; + } + +#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) + LOG_PRINT_L1("Creating block template: reward " << block_reward << + ", fee " << fee); +#endif + block_reward += fee; + + // from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and + // keeps the paid amount almost the same. The unpaid remainder gets pushed back to the + // emission schedule + // from hard fork 4, we use a single "dusty" output. This makes the tx even smaller, + // and avoids the quantization. These outputs will be added as rct outputs with identity + // masks, to they can be used as rct inputs. + if (hard_fork_version >= 2 && hard_fork_version < 4) { + block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD; + } + + std::vector<uint64_t> out_amounts; + decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD, + [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, + [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); + + CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero"); + if (height == 0 || hard_fork_version >= 4) + { + // the genesis block was not decomposed, for unknown reasons + while (max_outs < out_amounts.size()) + { + //out_amounts[out_amounts.size() - 2] += out_amounts.back(); + //out_amounts.resize(out_amounts.size() - 1); + out_amounts[1] += out_amounts[0]; + for (size_t n = 1; n < out_amounts.size(); ++n) + out_amounts[n - 1] = out_amounts[n]; + out_amounts.resize(out_amounts.size() - 1); + } + } + else + { + CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded"); + } + + uint64_t summary_amounts = 0; + for (size_t no = 0; no < out_amounts.size(); no++) + { + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);; + crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); + bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")"); + + r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")"); + + txout_to_key tk; + tk.key = out_eph_public_key; + + tx_out out; + summary_amounts += out.amount = out_amounts[no]; + out.target = tk; + tx.vout.push_back(out); + } + + CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); + + if (hard_fork_version >= 4) + tx.version = 2; + else + tx.version = 1; + + //lock + tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; + tx.vin.push_back(in); + + tx.invalidate_hashes(); + + //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee) + // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2); + return true; + } + //--------------------------------------------------------------- + crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys) + { + if (destinations.empty()) + return null_pkey; + for (size_t n = 1; n < destinations.size(); ++n) + { + if (!memcmp(&destinations[n].addr, &sender_keys.m_account_address, sizeof(destinations[0].addr))) + continue; + if (destinations[n].amount == 0) + continue; + if (memcmp(&destinations[n].addr, &destinations[0].addr, sizeof(destinations[0].addr))) + return null_pkey; + } + return destinations[0].addr.m_view_public_key; + } + //--------------------------------------------------------------- + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct) + { + std::vector<rct::key> amount_keys; + tx.set_null(); + amount_keys.clear(); + + tx.version = rct ? 2 : 1; + tx.unlock_time = unlock_time; + + tx.extra = extra; + keypair txkey = keypair::generate(); + remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key)); + add_tx_pub_key_to_extra(tx, txkey.pub); + tx_key = txkey.sec; + + // if we have a stealth payment id, find it and encrypt it with the tx key now + std::vector<tx_extra_field> tx_extra_fields; + if (parse_tx_extra(tx.extra, tx_extra_fields)) + { + tx_extra_nonce extra_nonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + crypto::hash8 payment_id = null_hash8; + if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + LOG_PRINT_L2("Encrypting payment id " << payment_id); + crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, sender_account_keys); + if (view_key_pub == null_pkey) + { + LOG_ERROR("Destinations have to have exactly one output to support encrypted payment ids"); + return false; + } + + if (!encrypt_payment_id(payment_id, view_key_pub, txkey.sec)) + { + LOG_ERROR("Failed to encrypt payment id"); + return false; + } + + std::string extra_nonce; + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); + remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce)); + if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) + { + LOG_ERROR("Failed to add encrypted payment id to tx extra"); + return false; + } + LOG_PRINT_L1("Encrypted payment ID: " << payment_id); + } + } + } + else + { + LOG_ERROR("Failed to parse tx extra"); + return false; + } + + struct input_generation_context_data + { + keypair in_ephemeral; + }; + std::vector<input_generation_context_data> in_contexts; + + uint64_t summary_inputs_money = 0; + //fill inputs + int idx = -1; + for(const tx_source_entry& src_entr: sources) + { + ++idx; + if(src_entr.real_output >= src_entr.outputs.size()) + { + LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size()); + return false; + } + summary_inputs_money += src_entr.amount; + + //key_derivation recv_derivation; + in_contexts.push_back(input_generation_context_data()); + keypair& in_ephemeral = in_contexts.back().in_ephemeral; + crypto::key_image img; + if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) + return false; + + //check that derivated key is equal with real output key + if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) ) + { + LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:" + << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" + << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second) ); + LOG_ERROR("amount " << src_entr.amount << ", rct " << src_entr.rct); + LOG_ERROR("tx pubkey " << src_entr.real_out_tx_key << ", real_output_in_tx_index " << src_entr.real_output_in_tx_index); + return false; + } + + //put key image into tx input + txin_to_key input_to_key; + input_to_key.amount = src_entr.amount; + input_to_key.k_image = img; + + //fill outputs array and use relative offsets + for(const tx_source_entry::output_entry& out_entry: src_entr.outputs) + input_to_key.key_offsets.push_back(out_entry.first); + + input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); + tx.vin.push_back(input_to_key); + } + + // "Shuffle" outs + std::vector<tx_destination_entry> shuffled_dsts(destinations); + std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } ); + + uint64_t summary_outs_money = 0; + //fill outputs + size_t output_index = 0; + for(const tx_destination_entry& dst_entr: shuffled_dsts) + { + CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount); + crypto::key_derivation derivation; + crypto::public_key out_eph_public_key; + bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")"); + + if (tx.version > 1) + { + crypto::secret_key scalar1; + crypto::derivation_to_scalar(derivation, output_index, scalar1); + amount_keys.push_back(rct::sk2rct(scalar1)); + } + r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); + + tx_out out; + out.amount = dst_entr.amount; + txout_to_key tk; + tk.key = out_eph_public_key; + out.target = tk; + tx.vout.push_back(out); + output_index++; + summary_outs_money += dst_entr.amount; + } + + //check money + if(summary_outs_money > summary_inputs_money ) + { + LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"); + return false; + } + + // check for watch only wallet + bool zero_secret_key = true; + for (size_t i = 0; i < sizeof(sender_account_keys.m_spend_secret_key); ++i) + zero_secret_key &= (sender_account_keys.m_spend_secret_key.data[i] == 0); + if (zero_secret_key) + { + MDEBUG("Null secret key, skipping signatures"); + } + + if (tx.version == 1) + { + //generate ring signatures + crypto::hash tx_prefix_hash; + get_transaction_prefix_hash(tx, tx_prefix_hash); + + std::stringstream ss_ring_s; + size_t i = 0; + for(const tx_source_entry& src_entr: sources) + { + ss_ring_s << "pub_keys:" << ENDL; + std::vector<const crypto::public_key*> keys_ptrs; + std::vector<crypto::public_key> keys(src_entr.outputs.size()); + size_t ii = 0; + for(const tx_source_entry::output_entry& o: src_entr.outputs) + { + keys[ii] = rct2pk(o.second.dest); + keys_ptrs.push_back(&keys[ii]); + ss_ring_s << o.second.dest << ENDL; + ++ii; + } + + tx.signatures.push_back(std::vector<crypto::signature>()); + std::vector<crypto::signature>& sigs = tx.signatures.back(); + sigs.resize(src_entr.outputs.size()); + if (!zero_secret_key) + crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); + ss_ring_s << "signatures:" << ENDL; + std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;}); + ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL; + i++; + } + + MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str()); + } + else + { + size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct + + // the non-simple version is slightly smaller, but assumes all real inputs + // are on the same index, so can only be used if there just one ring. + bool use_simple_rct = sources.size() > 1; + + if (!use_simple_rct) + { + // non simple ringct requires all real inputs to be at the same index for all inputs + for(const tx_source_entry& src_entr: sources) + { + if(src_entr.real_output != sources.begin()->real_output) + { + LOG_ERROR("All inputs must have the same index for non-simple ringct"); + return false; + } + } + + // enforce same mixin for all outputs + for (size_t i = 1; i < sources.size(); ++i) { + if (n_total_outs != sources[i].outputs.size()) { + LOG_ERROR("Non-simple ringct transaction has varying mixin"); + return false; + } + } + } + + uint64_t amount_in = 0, amount_out = 0; + rct::ctkeyV inSk; + // mixRing indexing is done the other way round for simple + rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs); + rct::keyV destinations; + std::vector<uint64_t> inamounts, outamounts; + std::vector<unsigned int> index; + for (size_t i = 0; i < sources.size(); ++i) + { + rct::ctkey ctkey; + amount_in += sources[i].amount; + inamounts.push_back(sources[i].amount); + index.push_back(sources[i].real_output); + // inSk: (secret key, mask) + ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec); + ctkey.mask = sources[i].mask; + inSk.push_back(ctkey); + // inPk: (public key, commitment) + // will be done when filling in mixRing + } + for (size_t i = 0; i < tx.vout.size(); ++i) + { + destinations.push_back(rct::pk2rct(boost::get<txout_to_key>(tx.vout[i].target).key)); + outamounts.push_back(tx.vout[i].amount); + amount_out += tx.vout[i].amount; + } + + if (use_simple_rct) + { + // mixRing indexing is done the other way round for simple + for (size_t i = 0; i < sources.size(); ++i) + { + mixRing[i].resize(sources[i].outputs.size()); + for (size_t n = 0; n < sources[i].outputs.size(); ++n) + { + mixRing[i][n] = sources[i].outputs[n].second; + } + } + } + else + { + for (size_t i = 0; i < n_total_outs; ++i) // same index assumption + { + mixRing[i].resize(sources.size()); + for (size_t n = 0; n < sources.size(); ++n) + { + mixRing[i][n] = sources[n].outputs[i].second; + } + } + } + + // fee + if (!use_simple_rct && amount_in > amount_out) + outamounts.push_back(amount_in - amount_out); + + // zero out all amounts to mask rct outputs, real amounts are now encrypted + for (size_t i = 0; i < tx.vin.size(); ++i) + { + if (sources[i].rct) + boost::get<txin_to_key>(tx.vin[i]).amount = 0; + } + for (size_t i = 0; i < tx.vout.size(); ++i) + tx.vout[i].amount = 0; + + crypto::hash tx_prefix_hash; + get_transaction_prefix_hash(tx, tx_prefix_hash); + rct::ctkeyV outSk; + if (use_simple_rct) + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk); + else + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk); // same index assumption + + CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); + + MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL); + } + + tx.invalidate_hashes(); + + return true; + } + //--------------------------------------------------------------- + bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time) + { + crypto::secret_key tx_key; + return construct_tx_and_get_tx_key(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key); + } + //--------------------------------------------------------------- + bool generate_genesis_block( + block& bl + , std::string const & genesis_tx + , uint32_t nonce + ) + { + //genesis block + bl = boost::value_initialized<block>(); + + + account_public_address ac = boost::value_initialized<account_public_address>(); + std::vector<size_t> sz; + construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis + blobdata txb = tx_to_blob(bl.miner_tx); + std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); + + std::string genesis_coinbase_tx_hex = config::GENESIS_TX; + + blobdata tx_bl; + string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); + bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); + CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); + bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; + bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; + bl.timestamp = 0; + bl.nonce = nonce; + miner::find_nonce_for_given_block(bl, 1, 0); + bl.invalidate_hashes(); + return true; + } + //--------------------------------------------------------------- +} diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h new file mode 100644 index 000000000..933070e1e --- /dev/null +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -0,0 +1,101 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once +#include "cryptonote_basic/cryptonote_format_utils.h" +#include <boost/serialization/vector.hpp> +#include <boost/serialization/utility.hpp> + +namespace cryptonote +{ + //--------------------------------------------------------------- + bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); + + struct tx_source_entry + { + typedef std::pair<uint64_t, rct::ctkey> output_entry; + + std::vector<output_entry> outputs; //index + key + optional ringct commitment + size_t real_output; //index in outputs vector of real output_entry + crypto::public_key real_out_tx_key; //incoming real tx public key + size_t real_output_in_tx_index; //index in transaction outputs vector + uint64_t amount; //money + bool rct; //true if the output is rct + rct::key mask; //ringct amount mask + + void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); } + }; + + struct tx_destination_entry + { + uint64_t amount; //money + account_public_address addr; //destination address + + tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)) { } + tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { } + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(amount) + FIELD(addr) + END_SERIALIZE() + }; + + //--------------------------------------------------------------- + crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys); + bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time); + bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false); + + bool generate_genesis_block( + block& bl + , std::string const & genesis_tx + , uint32_t nonce + ); + +} + +BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0) + +namespace boost +{ + namespace serialization + { + template <class Archive> + inline void serialize(Archive &a, cryptonote::tx_source_entry &x, const boost::serialization::version_type ver) + { + a & x.outputs; + a & x.real_output; + a & x.real_out_tx_key; + a & x.real_output_in_tx_index; + a & x.amount; + a & x.rct; + a & x.mask; + } + } +} diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp deleted file mode 100644 index 88c631f80..000000000 --- a/src/cryptonote_core/miner.cpp +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright (c) 2014-2016, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -#include <sstream> -#include <numeric> -#include <boost/utility/value_init.hpp> -#include <boost/interprocess/detail/atomic.hpp> -#include <boost/limits.hpp> -#include "misc_language.h" -#include "include_base_utils.h" -#include "cryptonote_basic_impl.h" -#include "cryptonote_format_utils.h" -#include "file_io_utils.h" -#include "common/command_line.h" -#include "string_coding.h" -#include "storages/portable_storage_template_helper.h" - -#undef MONERO_DEFAULT_LOG_CATEGORY -#define MONERO_DEFAULT_LOG_CATEGORY "miner" - -using namespace epee; - -#include "miner.h" - - -extern "C" void slow_hash_allocate_state(); -extern "C" void slow_hash_free_state(); -namespace cryptonote -{ - - namespace - { - const command_line::arg_descriptor<std::string> arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; - const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; - const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; - } - - - miner::miner(i_miner_handler* phandler):m_stop(1), - m_template(boost::value_initialized<block>()), - m_template_no(0), - m_diffic(0), - m_thread_index(0), - m_phandler(phandler), - m_height(0), - m_pausers_count(0), - m_threads_total(0), - m_starter_nonce(0), - m_last_hr_merge_time(0), - m_hashes(0), - m_do_print_hashrate(false), - m_do_mining(false), - m_current_hash_rate(0) - { - - } - //----------------------------------------------------------------------------------------------------- - miner::~miner() - { - stop(); - } - //----------------------------------------------------------------------------------------------------- - bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height) - { - CRITICAL_REGION_LOCAL(m_template_lock); - m_template = bl; - m_diffic = di; - m_height = height; - ++m_template_no; - m_starter_nonce = crypto::rand<uint32_t>(); - return true; - } - //----------------------------------------------------------------------------------------------------- - bool miner::on_block_chain_update() - { - if(!is_mining()) - return true; - - return request_block_template(); - } - //----------------------------------------------------------------------------------------------------- - bool miner::request_block_template() - { - block bl = AUTO_VAL_INIT(bl); - difficulty_type di = AUTO_VAL_INIT(di); - uint64_t height = AUTO_VAL_INIT(height); - cryptonote::blobdata extra_nonce; - if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) - { - extra_nonce = m_extra_messages[m_config.current_extra_message_index]; - } - - if(!m_phandler->get_block_template(bl, m_mine_address, di, height, extra_nonce)) - { - LOG_ERROR("Failed to get_block_template(), stopping mining"); - return false; - } - set_block_template(bl, di, height); - return true; - } - //----------------------------------------------------------------------------------------------------- - bool miner::on_idle() - { - m_update_block_template_interval.do_call([&](){ - if(is_mining())request_block_template(); - return true; - }); - - m_update_merge_hr_interval.do_call([&](){ - merge_hr(); - return true; - }); - - return true; - } - //----------------------------------------------------------------------------------------------------- - void miner::do_print_hashrate(bool do_hr) - { - m_do_print_hashrate = do_hr; - } - //----------------------------------------------------------------------------------------------------- - void miner::merge_hr() - { - if(m_last_hr_merge_time && is_mining()) - { - m_current_hash_rate = m_hashes * 1000 / ((misc_utils::get_tick_count() - m_last_hr_merge_time + 1)); - CRITICAL_REGION_LOCAL(m_last_hash_rates_lock); - m_last_hash_rates.push_back(m_current_hash_rate); - if(m_last_hash_rates.size() > 19) - m_last_hash_rates.pop_front(); - if(m_do_print_hashrate) - { - uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0); - float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size()); - std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << ENDL; - } - } - m_last_hr_merge_time = misc_utils::get_tick_count(); - m_hashes = 0; - } - //----------------------------------------------------------------------------------------------------- - void miner::init_options(boost::program_options::options_description& desc) - { - command_line::add_arg(desc, arg_extra_messages); - command_line::add_arg(desc, arg_start_mining); - command_line::add_arg(desc, arg_mining_threads); - } - //----------------------------------------------------------------------------------------------------- - bool miner::init(const boost::program_options::variables_map& vm, bool testnet) - { - if(command_line::has_arg(vm, arg_extra_messages)) - { - std::string buff; - bool r = file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_extra_messages), buff); - CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << command_line::get_arg(vm, arg_extra_messages)); - std::vector<std::string> extra_vec; - boost::split(extra_vec, buff, boost::is_any_of("\n"), boost::token_compress_on ); - m_extra_messages.resize(extra_vec.size()); - for(size_t i = 0; i != extra_vec.size(); i++) - { - string_tools::trim(extra_vec[i]); - if(!extra_vec[i].size()) - continue; - std::string buff = string_encoding::base64_decode(extra_vec[i]); - if(buff != "0") - m_extra_messages[i] = buff; - } - m_config_folder_path = boost::filesystem::path(command_line::get_arg(vm, arg_extra_messages)).parent_path().string(); - m_config = AUTO_VAL_INIT(m_config); - epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); - MINFO("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index); - } - - if(command_line::has_arg(vm, arg_start_mining)) - { - if(!cryptonote::get_account_address_from_str(m_mine_address, testnet, command_line::get_arg(vm, arg_start_mining))) - { - LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled"); - return false; - } - m_threads_total = 1; - m_do_mining = true; - if(command_line::has_arg(vm, arg_mining_threads)) - { - m_threads_total = command_line::get_arg(vm, arg_mining_threads); - } - } - - return true; - } - //----------------------------------------------------------------------------------------------------- - bool miner::is_mining() const - { - return !m_stop; - } - //----------------------------------------------------------------------------------------------------- - const account_public_address& miner::get_mining_address() const - { - return m_mine_address; - } - //----------------------------------------------------------------------------------------------------- - uint32_t miner::get_threads_count() const { - return m_threads_total; - } - //----------------------------------------------------------------------------------------------------- - bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs) - { - m_mine_address = adr; - m_threads_total = static_cast<uint32_t>(threads_count); - m_starter_nonce = crypto::rand<uint32_t>(); - CRITICAL_REGION_LOCAL(m_threads_lock); - if(is_mining()) - { - LOG_ERROR("Starting miner but it's already started"); - return false; - } - - if(!m_threads.empty()) - { - LOG_ERROR("Unable to start miner because there are active mining threads"); - return false; - } - - if(!m_template_no) - request_block_template();//lets update block template - - boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0); - boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0); - - for(size_t i = 0; i != threads_count; i++) - { - m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this))); - } - - LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" ); - return true; - } - //----------------------------------------------------------------------------------------------------- - uint64_t miner::get_speed() const - { - if(is_mining()) { - return m_current_hash_rate; - } - else { - return 0; - } - } - //----------------------------------------------------------------------------------------------------- - void miner::send_stop_signal() - { - boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1); - } - //----------------------------------------------------------------------------------------------------- - bool miner::stop() - { - MTRACE("Miner has received stop signal"); - - if (!is_mining()) - { - MDEBUG("Not mining - nothing to stop" ); - return true; - } - - send_stop_signal(); - CRITICAL_REGION_LOCAL(m_threads_lock); - - for(boost::thread& th: m_threads) - th.join(); - - MINFO("Mining has been stopped, " << m_threads.size() << " finished" ); - m_threads.clear(); - return true; - } - //----------------------------------------------------------------------------------------------------- - bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height) - { - for(; bl.nonce != std::numeric_limits<uint32_t>::max(); bl.nonce++) - { - crypto::hash h; - get_block_longhash(bl, h, height); - - if(check_hash(h, diffic)) - { - return true; - } - } - return false; - } - //----------------------------------------------------------------------------------------------------- - void miner::on_synchronized() - { - if(m_do_mining) - { - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - - start(m_mine_address, m_threads_total, attrs); - } - } - //----------------------------------------------------------------------------------------------------- - void miner::pause() - { - CRITICAL_REGION_LOCAL(m_miners_count_lock); - ++m_pausers_count; - if(m_pausers_count == 1 && is_mining()) - MDEBUG("MINING PAUSED"); - } - //----------------------------------------------------------------------------------------------------- - void miner::resume() - { - CRITICAL_REGION_LOCAL(m_miners_count_lock); - --m_pausers_count; - if(m_pausers_count < 0) - { - m_pausers_count = 0; - MERROR("Unexpected miner::resume() called"); - } - if(!m_pausers_count && is_mining()) - MDEBUG("MINING RESUMED"); - } - //----------------------------------------------------------------------------------------------------- - bool miner::worker_thread() - { - uint32_t th_local_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index); - MGINFO("Miner thread was started ["<< th_local_index << "]"); - MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(th_local_index) + "]"); - uint32_t nonce = m_starter_nonce + th_local_index; - uint64_t height = 0; - difficulty_type local_diff = 0; - uint32_t local_template_ver = 0; - block b; - slow_hash_allocate_state(); - while(!m_stop) - { - if(m_pausers_count)//anti split workaround - { - misc_utils::sleep_no_w(100); - continue; - } - - if(local_template_ver != m_template_no) - { - CRITICAL_REGION_BEGIN(m_template_lock); - b = m_template; - local_diff = m_diffic; - height = m_height; - CRITICAL_REGION_END(); - local_template_ver = m_template_no; - nonce = m_starter_nonce + th_local_index; - } - - if(!local_template_ver)//no any set_block_template call - { - LOG_PRINT_L2("Block template not set yet"); - epee::misc_utils::sleep_no_w(1000); - continue; - } - - b.nonce = nonce; - crypto::hash h; - get_block_longhash(b, h, height); - - if(check_hash(h, local_diff)) - { - //we lucky! - ++m_config.current_extra_message_index; - MGINFO_GREEN("Found block for difficulty: " << local_diff); - if(!m_phandler->handle_block_found(b)) - { - --m_config.current_extra_message_index; - }else - { - //success update, lets update config - if (!m_config_folder_path.empty()) - epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); - } - } - nonce+=m_threads_total; - ++m_hashes; - } - slow_hash_free_state(); - MGINFO("Miner thread stopped ["<< th_local_index << "]"); - return true; - } - //----------------------------------------------------------------------------------------------------- -} - diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index e37ddec0d..f78f673c7 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,8 +34,8 @@ #include <vector> #include "tx_pool.h" -#include "cryptonote_format_utils.h" -#include "cryptonote_boost_serialization.h" +#include "cryptonote_tx_utils.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_config.h" #include "blockchain.h" #include "common/boost_serialization_helper.h" @@ -59,8 +59,6 @@ namespace cryptonote // codebase. As it stands, it is at best nontrivial to test // whether or not changing these parameters (or adding new) // will work correctly. - size_t const TRANSACTION_SIZE_LIMIT_V1 = (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); - size_t const TRANSACTION_SIZE_LIMIT_V2 = (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); time_t const MIN_RELAY_TIME = (60 * 5); // only start re-relaying transactions after that many seconds time_t const MAX_RELAY_TIME = (60 * 60 * 4); // at most that many seconds between resends float const ACCEPT_THRESHOLD = 1.0f; @@ -78,6 +76,11 @@ namespace cryptonote { return amount * ACCEPT_THRESHOLD; } + + uint64_t get_transaction_size_limit(uint8_t version) + { + return get_min_block_size(version) * 125 / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + } } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- @@ -149,7 +152,7 @@ namespace cryptonote return false; } - size_t tx_size_limit = (version < 2 ? TRANSACTION_SIZE_LIMIT_V1 : TRANSACTION_SIZE_LIMIT_V2); + size_t tx_size_limit = get_transaction_size_limit(version); if (!kept_by_block && blob_size >= tx_size_limit) { LOG_PRINT_L1("transaction is too big: " << blob_size << " bytes, maximum size: " << tx_size_limit); @@ -255,6 +258,7 @@ namespace cryptonote m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)blob_size, receive_time), id); + MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size)); return true; } //--------------------------------------------------------------------------------- @@ -419,6 +423,13 @@ namespace cryptonote txs.push_back(tx_vt.second.tx); } //------------------------------------------------------------------ + void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs) const + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + for(const auto& tx_vt: m_transactions) + txs.push_back(get_transaction_hash(tx_vt.second.tx)); + } + //------------------------------------------------------------------ //TODO: investigate whether boolean return is appropriate bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const { @@ -602,7 +613,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint8_t version) + bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version) { // Warning: This function takes already_generated_ // coins as an argument and appears to do nothing @@ -610,7 +621,7 @@ namespace cryptonote CRITICAL_REGION_LOCAL(m_transactions_lock); - uint64_t best_coinbase = 0; + uint64_t best_coinbase = 0, coinbase = 0; total_size = 0; fee = 0; @@ -618,7 +629,9 @@ namespace cryptonote get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version); - size_t max_total_size = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t max_total_size_pre_v5 = (130 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t max_total_size_v5 = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t max_total_size = version >= 5 ? max_total_size_v5 : max_total_size_pre_v5; std::unordered_set<crypto::key_image> k_images; LOG_PRINT_L2("Filling block template, median size " << median_size << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool"); @@ -636,29 +649,49 @@ namespace cryptonote continue; } - // If we're getting lower coinbase tx, - // stop including more tx - uint64_t block_reward; - if(!get_block_reward(median_size, total_size + tx_it->second.blob_size, already_generated_coins, block_reward, version)) + // start using the optimal filling algorithm from v5 + if (version >= 5) { - LOG_PRINT_L2(" would exceed maximum block size"); - sorted_it++; - continue; + // If we're getting lower coinbase tx, + // stop including more tx + uint64_t block_reward; + if(!get_block_reward(median_size, total_size + tx_it->second.blob_size, already_generated_coins, block_reward, version)) + { + LOG_PRINT_L2(" would exceed maximum block size"); + sorted_it++; + continue; + } + coinbase = block_reward + fee + tx_it->second.fee; + if (coinbase < template_accept_threshold(best_coinbase)) + { + LOG_PRINT_L2(" would decrease coinbase to " << print_money(coinbase)); + sorted_it++; + continue; + } } - uint64_t coinbase = block_reward + fee + tx_it->second.fee; - if (coinbase < template_accept_threshold(best_coinbase)) + else { - LOG_PRINT_L2(" would decrease coinbase to " << print_money(coinbase)); - sorted_it++; - continue; + // If we've exceeded the penalty free size, + // stop including more tx + if (total_size > median_size) + { + LOG_PRINT_L2(" would exceed median block size"); + break; + } } // Skip transactions that are not ready to be // included into the blockchain or that are // missing key images - if (!is_transaction_ready_to_go(tx_it->second) || have_key_images(k_images, tx_it->second.tx)) + if (!is_transaction_ready_to_go(tx_it->second)) { - LOG_PRINT_L2(" not ready to go, or key images already seen"); + LOG_PRINT_L2(" not ready to go"); + sorted_it++; + continue; + } + if (have_key_images(k_images, tx_it->second.tx)) + { + LOG_PRINT_L2(" key images already seen"); sorted_it++; continue; } @@ -672,6 +705,7 @@ namespace cryptonote LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)); } + expected_reward = best_coinbase; LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase) << " (including " << print_money(fee) << " in fees)"); @@ -682,15 +716,24 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); size_t n_removed = 0; - size_t tx_size_limit = (version < 2 ? TRANSACTION_SIZE_LIMIT_V1 : TRANSACTION_SIZE_LIMIT_V2); + size_t tx_size_limit = get_transaction_size_limit(version); for (auto it = m_transactions.begin(); it != m_transactions.end(); ) { + bool remove = false; + const crypto::hash &txid = get_transaction_hash(it->second.tx); if (it->second.blob_size >= tx_size_limit) { - LOG_PRINT_L1("Transaction " << get_transaction_hash(it->second.tx) << " is too big (" << it->second.blob_size << " bytes), removing it from pool"); + LOG_PRINT_L1("Transaction " << txid << " is too big (" << it->second.blob_size << " bytes), removing it from pool"); + remove = true; + } + else if (m_blockchain.have_tx(txid)) { + LOG_PRINT_L1("Transaction " << txid << " is in the blockchain, removing it from pool"); + remove = true; + } + if (remove) { remove_transaction_keyimages(it->second.tx); - auto sorted_it = find_tx_in_sorted_container(it->first); + auto sorted_it = find_tx_in_sorted_container(txid); if (sorted_it == m_txs_by_fee_and_receive_time.end()) { - LOG_PRINT_L1("Removing tx " << it->first << " from tx pool, but it was not found in the sorted txs container!"); + LOG_PRINT_L1("Removing tx " << txid << " from tx pool, but it was not found in the sorted txs container!"); } else { diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 2712f75bb..f68bc0bb9 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -41,8 +41,8 @@ #include "string_tools.h" #include "syncobj.h" #include "math_helper.h" -#include "cryptonote_basic_impl.h" -#include "verification_context.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/verification_context.h" #include "crypto/hash.h" #include "rpc/core_rpc_server_commands_defs.h" @@ -220,11 +220,12 @@ namespace cryptonote * @param already_generated_coins the current total number of coins "minted" * @param total_size return-by-reference the total size of the new block * @param fee return-by-reference the total of fees from the included transactions + * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param version hard fork version to use for consensus rules * * @return true */ - bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint8_t version); + bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version); /** * @brief get a list of all transactions in the pool @@ -234,6 +235,13 @@ namespace cryptonote void get_transactions(std::list<transaction>& txs) const; /** + * @brief get a list of all transaction hashes in the pool + * + * @param txs return-by-reference the list of transactions + */ + void get_transaction_hashes(std::vector<crypto::hash>& txs) const; + + /** * @brief get information about all transactions and key images in the pool * * see documentation on tx_info and spent_key_image_info for more details diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt index 65473b117..4ce380a48 100644 --- a/src/cryptonote_protocol/CMakeLists.txt +++ b/src/cryptonote_protocol/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -35,6 +35,8 @@ source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL}) #monero_private_headers(cryptonote_protocol ${CRYPTONOTE_PROTOCOL}) monero_add_library(cryptonote_protocol ${CRYPTONOTE_PROTOCOL}) target_link_libraries(cryptonote_protocol + PUBLIC + p2p PRIVATE ${EXTRA_LIBRARIES}) add_dependencies(cryptonote_protocol diff --git a/src/cryptonote_protocol/blobdatatype.h b/src/cryptonote_protocol/blobdatatype.h index 17285ee1c..2d12a84af 100644 --- a/src/cryptonote_protocol/blobdatatype.h +++ b/src/cryptonote_protocol/blobdatatype.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 7adc69080..fd5b980b8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,7 +32,7 @@ #include <list> #include "serialization/keyvalue_serialization.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_protocol/blobdatatype.h" namespace cryptonote { @@ -257,16 +257,16 @@ namespace cryptonote struct request { - block_complete_entry b; + crypto::hash block_hash; uint64_t current_blockchain_height; std::vector<size_t> missing_tx_indices; uint32_t hop; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(b) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices) - KV_SERIALIZE(hop) - KV_SERIALIZE(current_blockchain_height) + KV_SERIALIZE_VAL_POD_AS_BLOB(block_hash) + KV_SERIALIZE(current_blockchain_height) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices) + KV_SERIALIZE(hop) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp index 09c202e79..e31276031 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp +++ b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief This is the place to implement our handlers for protocol network actions, e.g. for ratelimit for download-requests -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -122,7 +122,7 @@ cryptonote_protocol_handler_base::~cryptonote_protocol_handler_base() { void cryptonote_protocol_handler_base::handler_request_blocks_history(std::list<crypto::hash>& ids) { using namespace epee::net_utils; MDEBUG("### ~~~RRRR~~~~ ### sending request (type 2), limit = " << ids.size()); - MWARNING("RATE LIMIT NOT IMPLEMENTED HERE YET (download at unlimited speed?)"); + MDEBUG("RATE LIMIT NOT IMPLEMENTED HERE YET (download at unlimited speed?)"); // TODO } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 4b2de39b9..9d8bc43c2 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -2,7 +2,7 @@ /// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote) /// @brief This is the orginal cryptonote protocol network-events handler, modified by us -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -42,9 +42,9 @@ #include "warnings.h" #include "cryptonote_protocol_defs.h" #include "cryptonote_protocol_handler_common.h" -#include "cryptonote_core/connection_context.h" -#include "cryptonote_core/cryptonote_stat_info.h" -#include "cryptonote_core/verification_context.h" +#include "cryptonote_basic/connection_context.h" +#include "cryptonote_basic/cryptonote_stat_info.h" +#include "cryptonote_basic/verification_context.h" // #include <netinet/in.h> #include <boost/circular_buffer.hpp> @@ -135,6 +135,7 @@ namespace cryptonote std::atomic<bool> m_synchronized; bool m_one_request = true; std::atomic<bool> m_stopping; + epee::critical_section m_sync_lock; boost::mutex m_buffer_mutex; double get_avg_block_size(); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 1309ff742..79578a34e 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2,7 +2,7 @@ /// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote) /// @brief This is the orginal cryptonote protocol network-events handler, modified by us -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -39,7 +39,7 @@ #include <list> #include <unordered_map> -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "profile_tools.h" #include "../../src/p2p/network_throttle-detail.hpp" @@ -265,19 +265,24 @@ namespace cryptonote if(context.m_state == cryptonote_connection_context::state_synchronizing) return true; + uint64_t target = m_core.get_target_blockchain_height(); + if (target == 0) + target = m_core.get_current_blockchain_height(); + if(m_core.have_block(hshd.top_id)) { context.m_state = cryptonote_connection_context::state_normal; - if(is_inital) + if(is_inital && target == m_core.get_current_blockchain_height()) on_connection_synchronized(); return true; } + if (hshd.current_height > target) + { /* As I don't know if accessing hshd from core could be a good practice, I prefer pushing target height to the core at the same time it is pushed to the user. Nz. */ m_core.set_target_blockchain_height(static_cast<int64_t>(hshd.current_height)); - int64_t diff = static_cast<int64_t>(hshd.current_height) - static_cast<int64_t>(m_core.get_current_blockchain_height()); int64_t max_block_height = max(static_cast<int64_t>(hshd.current_height),static_cast<int64_t>(m_core.get_current_blockchain_height())); int64_t last_block_v1 = 1009826; @@ -286,6 +291,7 @@ namespace cryptonote << " [Your node is " << std::abs(diff) << " blocks (" << ((abs(diff) - diff_v2) / (24 * 60 * 60 / DIFFICULTY_TARGET_V1)) + (diff_v2 / (24 * 60 * 60 / DIFFICULTY_TARGET_V2)) << " days) " << (0 <= diff ? std::string("behind") : std::string("ahead")) << "] " << ENDL << "SYNCHRONIZATION started"); + } LOG_PRINT_L1("Remote blockchain height: " << hshd.current_height << ", id: " << hshd.top_id); context.m_state = cryptonote_connection_context::state_synchronizing; context.m_remote_blockchain_height = hshd.current_height; @@ -367,7 +373,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context) { - MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (hop " << arg.hop << ", " << arg.b.txs.size() << " txes)"); + MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (height " << arg.current_blockchain_height << ", hop " << arg.hop << ", " << arg.b.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; @@ -377,7 +383,7 @@ namespace cryptonote transaction miner_tx; if(parse_and_validate_block_from_blob(arg.b.block, new_block)) { - // This is a seccond notification, we must have asked for some missing tx + // This is a second notification, we must have asked for some missing tx if(!context.m_requested_objects.empty()) { // What we asked for != to what we received .. @@ -475,6 +481,7 @@ namespace cryptonote // sent in our pool, so don't verify again.. if(!m_core.get_pool_transaction(tx_hash, tx)) { + MDEBUG("Incoming tx " << tx_hash << " not in pool, adding"); cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); if(!m_core.handle_incoming_tx(tx_blob, tvc, true, true, false) || tvc.m_verifivation_failed) { @@ -496,9 +503,9 @@ namespace cryptonote { LOG_ERROR_CCONTEXT ( - "sent wrong tx: failed to parse and validate transaction: \r\n" + "sent wrong tx: failed to parse and validate transaction: " << epee::string_tools::buff_to_hex_nodelimer(tx_blob) - << "\r\n dropping connection" + << ", dropping connection" ); m_p2p->drop_connection(context); @@ -535,7 +542,28 @@ namespace cryptonote } else { - need_tx_indices.push_back(tx_idx); + std::vector<crypto::hash> tx_ids; + std::list<transaction> txes; + std::list<crypto::hash> missing; + tx_ids.push_back(tx_hash); + if (m_core.get_transactions(tx_ids, txes, missing) && missing.empty()) + { + if (txes.size() == 1) + { + have_tx.push_back(tx_to_blob(txes.front())); + } + else + { + MERROR("1 tx requested, none not found, but " << txes.size() << " returned"); + m_core.resume_mine(); + return 1; + } + } + else + { + MDEBUG("Tx " << tx_hash << " not found in pool"); + need_tx_indices.push_back(tx_idx); + } } ++tx_idx; @@ -544,8 +572,11 @@ namespace cryptonote if(!need_tx_indices.empty()) // drats, we don't have everything.. { // request non-mempool txs + MDEBUG("We are missing " << need_tx_indices.size() << " txes for this fluffy block"); + for (auto txidx: need_tx_indices) + MDEBUG(" tx " << new_block.tx_hashes[txidx]); NOTIFY_REQUEST_FLUFFY_MISSING_TX::request missing_tx_req; - missing_tx_req.b = arg.b; + missing_tx_req.block_hash = get_block_hash(new_block); missing_tx_req.hop = arg.hop; missing_tx_req.current_blockchain_height = arg.current_blockchain_height; missing_tx_req.missing_tx_indices = std::move(need_tx_indices); @@ -555,6 +586,8 @@ namespace cryptonote } else // whoo-hoo we've got em all .. { + MDEBUG("We have all needed txes for this fluffy block"); + block_complete_entry b; b.block = arg.b.block; b.txs = have_tx; @@ -581,7 +614,7 @@ namespace cryptonote NOTIFY_NEW_BLOCK::request reg_arg = AUTO_VAL_INIT(reg_arg); reg_arg.hop = arg.hop; reg_arg.current_blockchain_height = arg.current_blockchain_height; - reg_arg.b.block = b.block; + reg_arg.b = b; relay_block(reg_arg, context); } else if( bvc.m_marked_as_orphaned ) @@ -598,9 +631,9 @@ namespace cryptonote { LOG_ERROR_CCONTEXT ( - "sent wrong block: failed to parse and validate block: \r\n" + "sent wrong block: failed to parse and validate block: " << epee::string_tools::buff_to_hex_nodelimer(arg.b.block) - << "\r\n dropping connection" + << ", dropping connection" ); m_core.resume_mine(); @@ -615,34 +648,32 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context) { - MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes)"); + MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes), block hash " << arg.block_hash); - std::list<block> local_blocks; - std::list<transaction> local_txs; - if(!m_core.get_blocks(arg.current_blockchain_height - 1, 1, local_blocks, local_txs)) - { + std::list<std::pair<cryptonote::blobdata, block>> local_blocks; + std::list<cryptonote::blobdata> local_txs; - LOG_ERROR_CCONTEXT - ( - "Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX" - << ", get_blocks( start_offset = " << (arg.current_blockchain_height - 1) << " ) failed" - << ", dropping connection" - ); - + block b; + if (!m_core.get_block_by_hash(arg.block_hash, b)) + { + LOG_ERROR_CCONTEXT("failed to find block: " << arg.block_hash << ", dropping connection"); m_p2p->drop_connection(context); - return 1; + return 1; } + for (auto txidx: arg.missing_tx_indices) + MDEBUG(" tx " << b.tx_hashes[txidx]); + + std::vector<crypto::hash> txids; NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_response; - fluffy_response.b = arg.b; - fluffy_response.current_blockchain_height = m_core.get_current_blockchain_height(); + fluffy_response.b.block = t_serializable_object_to_blob(b); + fluffy_response.current_blockchain_height = arg.current_blockchain_height; fluffy_response.hop = arg.hop; - size_t local_txs_count = local_txs.size(); for(auto& tx_idx: arg.missing_tx_indices) { - if(tx_idx < local_txs_count) + if(tx_idx < b.tx_hashes.size()) { - fluffy_response.b.txs.push_back(t_serializable_object_to_blob( *(std::next(local_txs.begin(), tx_idx)) )); + txids.push_back(b.tx_hashes[tx_idx]); } else { @@ -650,7 +681,8 @@ namespace cryptonote ( "Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX" << ", request is asking for a tx whose index is out of bounds " - << ", tx index = " << tx_idx << ", block_height = " << arg.current_blockchain_height + << ", tx index = " << tx_idx << ", block tx count " << b.tx_hashes.size() + << ", block_height = " << arg.current_blockchain_height << ", dropping connection" ); @@ -658,7 +690,29 @@ namespace cryptonote return 1; } } - + + std::list<cryptonote::transaction> txs; + std::list<crypto::hash> missed; + if (!m_core.get_transactions(txids, txs, missed)) + { + LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " + << "failed to get requested transactions"); + m_p2p->drop_connection(context); + return 1; + } + if (!missed.empty() || txs.size() != txids.size()) + { + LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " + << missed.size() << " requested transactions not found" << ", dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + for(auto& tx: txs) + { + fluffy_response.b.txs.push_back(t_serializable_object_to_blob(tx)); + } + LOG_PRINT_CCONTEXT_L2 ( "-->>NOTIFY_RESPONSE_FLUFFY_MISSING_TX: " @@ -790,6 +844,8 @@ namespace cryptonote context.m_remote_blockchain_height = arg.current_blockchain_height; size_t count = 0; + std::vector<crypto::hash> block_hashes; + block_hashes.reserve(arg.blocks.size()); for(const block_complete_entry& block_entry: arg.blocks) { if (m_stopping) @@ -801,15 +857,16 @@ namespace cryptonote block b; if(!parse_and_validate_block_from_blob(block_entry.block, b)) { - LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: \r\n" - << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << "\r\n dropping connection"); + LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: " + << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << ", dropping connection"); m_p2p->drop_connection(context); return 1; } //to avoid concurrency in core between connections, suspend connections which delivered block later then first one + const crypto::hash block_hash = get_block_hash(b); if(count == 2) { - if(m_core.have_block(get_block_hash(b))) + if(m_core.have_block(block_hash)) { context.m_state = cryptonote_connection_context::state_idle; context.m_needed_objects.clear(); @@ -819,7 +876,7 @@ namespace cryptonote } } - auto req_it = context.m_requested_objects.find(get_block_hash(b)); + auto req_it = context.m_requested_objects.find(block_hash); if(req_it == context.m_requested_objects.end()) { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) @@ -836,6 +893,7 @@ namespace cryptonote } context.m_requested_objects.erase(req_it); + block_hashes.push_back(block_hash); } if(context.m_requested_objects.size()) @@ -848,17 +906,44 @@ namespace cryptonote { - m_core.pause_mine(); - epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler( - boost::bind(&t_core::resume_mine, &m_core)); - MLOG_YELLOW(el::Level::Debug, "Got NEW BLOCKS inside of " << __FUNCTION__ << ": size: " << arg.blocks.size()); if (m_core.get_test_drop_download() && m_core.get_test_drop_download_height()) { // DISCARD BLOCKS for testing - uint64_t previous_height = m_core.get_current_blockchain_height(); + // we lock all the rest to avoid having multiple connections redo a lot + // of the same work, and one of them doing it for nothing: subsequent + // connections will wait until the current one's added its blocks, then + // will add any extra it has, if any + CRITICAL_REGION_LOCAL(m_sync_lock); + + m_core.pause_mine(); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler( + boost::bind(&t_core::resume_mine, &m_core)); + + const uint64_t previous_height = m_core.get_current_blockchain_height(); + + // dismiss what another connection might already have done (likely everything) + uint64_t top_height; + crypto::hash top_hash; + if (m_core.get_blockchain_top(top_height, top_hash)) { + uint64_t dismiss = 1; + for (const auto &h: block_hashes) { + if (top_hash == h) { + LOG_DEBUG_CC(context, "Found current top block in synced blocks, dismissing " + << dismiss << "/" << arg.blocks.size() << " blocks"); + while (dismiss--) + arg.blocks.pop_front(); + break; + } + ++dismiss; + } + } + + if (arg.blocks.empty()) + goto skip; m_core.prepare_handle_incoming_blocks(arg.blocks); + for(const block_complete_entry& block_entry: arg.blocks) { if (m_stopping) @@ -875,7 +960,7 @@ namespace cryptonote m_core.handle_incoming_tx(tx_blob, tvc, true, true, false); if(tvc.m_verifivation_failed) { - LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = " + LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, tx_id = " << epee::string_tools::pod_to_hex(get_blob_hash(tx_blob)) << ", dropping connection"); m_p2p->drop_connection(context); m_core.cleanup_handle_incoming_blocks(); @@ -922,6 +1007,7 @@ namespace cryptonote } +skip: request_missing_objects(context, true); return 1; } @@ -1071,9 +1157,9 @@ namespace cryptonote context.m_last_response_height = arg.start_height + arg.m_block_ids.size()-1; if(context.m_last_response_height > context.m_remote_blockchain_height) { - LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with \r\nm_total_height=" << arg.total_height - << "\r\nm_start_height=" << arg.start_height - << "\r\nm_block_ids.size()=" << arg.m_block_ids.size()); + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with m_total_height=" << arg.total_height + << ", m_start_height=" << arg.start_height + << ", m_block_ids.size()=" << arg.m_block_ids.size()); m_p2p->drop_connection(context); } @@ -1110,12 +1196,12 @@ namespace cryptonote { if(m_core.get_testnet() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS)) { - MDEBUG("PEER SUPPORTS FLUFFY BLOCKS - RELAYING THIN/COMPACT WHATEVER BLOCK"); + LOG_DEBUG_CC(context, "PEER SUPPORTS FLUFFY BLOCKS - RELAYING THIN/COMPACT WHATEVER BLOCK"); fluffyConnections.push_back(context.m_connection_id); } else { - MDEBUG("PEER DOESN'T SUPPORT FLUFFY BLOCKS - RELAYING FULL BLOCK"); + LOG_DEBUG_CC(context, "PEER DOESN'T SUPPORT FLUFFY BLOCKS - RELAYING FULL BLOCK"); fullConnections.push_back(context.m_connection_id); } } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index 06061f5d0..1163a0fe8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,7 +32,7 @@ #include "p2p/net_node_common.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/connection_context.h" +#include "cryptonote_basic/connection_context.h" namespace cryptonote { /************************************************************************/ diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 1b6363f7b..649823a59 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -29,9 +29,9 @@ set(blocksdat "") if(PER_BLOCK_CHECKPOINT) if(APPLE) - add_custom_command(OUTPUT blocksdat.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) else() - add_custom_command(OUTPUT blocksdat.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) endif() set(blocksdat "blocksdat.o") endif() diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index cb9fb6014..8eb3db195 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 27f9d0fd7..a7caeeffc 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -37,11 +37,11 @@ namespace daemonize { t_command_parser_executor::t_command_parser_executor( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& login , bool is_rpc , cryptonote::core_rpc_server* rpc_server ) - : m_executor(ip, port, user_agent, is_rpc, rpc_server) + : m_executor(ip, port, login, is_rpc, rpc_server) {} bool t_command_parser_executor::print_peer_list(const std::vector<std::string>& args) @@ -51,6 +51,13 @@ bool t_command_parser_executor::print_peer_list(const std::vector<std::string>& return m_executor.print_peer_list(); } +bool t_command_parser_executor::print_peer_list_stats(const std::vector<std::string>& args) +{ + if (!args.empty()) return false; + + return m_executor.print_peer_list_stats(); +} + bool t_command_parser_executor::save_blockchain(const std::vector<std::string>& args) { if (!args.empty()) return false; @@ -271,17 +278,30 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg if(testnet) std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl; uint64_t threads_count = 1; - if(args.size() > 2) + bool do_background_mining = false; + bool ignore_battery = false; + if(args.size() > 4) { return false; } - else if(args.size() == 2) + + if(args.size() == 4) + { + ignore_battery = args[3] == "true"; + } + + if(args.size() >= 3) + { + do_background_mining = args[2] == "true"; + } + + if(args.size() >= 2) { bool ok = epee::string_tools::get_xtype_from_string(threads_count, args[1]); threads_count = (ok && 0 < threads_count) ? threads_count : 1; } - m_executor.start_mining(adr, threads_count, testnet); + m_executor.start_mining(adr, threads_count, testnet, do_background_mining, ignore_battery); return true; } @@ -532,4 +552,30 @@ bool t_command_parser_executor::print_blockchain_dynamic_stats(const std::vector return m_executor.print_blockchain_dynamic_stats(nblocks); } +bool t_command_parser_executor::update(const std::vector<std::string>& args) +{ + if(args.size() != 1) + { + std::cout << "Exactly one parameter is needed: check, download, or update" << std::endl; + return false; + } + + return m_executor.update(args.front()); +} + +bool t_command_parser_executor::relay_tx(const std::vector<std::string>& args) +{ + if (args.size() != 1) return false; + + std::string txid; + crypto::hash hash; + if (!parse_hash256(args[0], hash)) + { + std::cout << "failed to parse tx id" << std::endl; + return true; + } + txid = args[0]; + return m_executor.relay_tx(txid); +} + } // namespace daemonize diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 15293ade9..a453553f1 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -6,7 +6,7 @@ */ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -36,7 +36,10 @@ #pragma once +#include <boost/optional/optional_fwd.hpp> + #include "daemon/rpc_command_executor.h" +#include "common/common_fwd.h" #include "rpc/core_rpc_server.h" namespace daemonize { @@ -49,13 +52,15 @@ public: t_command_parser_executor( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& login , bool is_rpc , cryptonote::core_rpc_server* rpc_server = NULL ); bool print_peer_list(const std::vector<std::string>& args); + bool print_peer_list_stats(const std::vector<std::string>& args); + bool save_blockchain(const std::vector<std::string>& args); bool show_hash_rate(const std::vector<std::string>& args); @@ -125,6 +130,10 @@ public: bool alt_chain_info(const std::vector<std::string>& args); bool print_blockchain_dynamic_stats(const std::vector<std::string>& args); + + bool update(const std::vector<std::string>& args); + + bool relay_tx(const std::vector<std::string>& args); }; } // namespace daemonize diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 95fd3178c..21f550a85 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -40,11 +40,11 @@ namespace p = std::placeholders; t_command_server::t_command_server( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& login , bool is_rpc , cryptonote::core_rpc_server* rpc_server ) - : m_parser(ip, port, user_agent, is_rpc, rpc_server) + : m_parser(ip, port, login, is_rpc, rpc_server) , m_command_lookup() , m_is_rpc(is_rpc) { @@ -69,6 +69,11 @@ t_command_server::t_command_server( , "Print peer list" ); m_command_lookup.set_handler( + "print_pl_stats" + , std::bind(&t_command_parser_executor::print_peer_list_stats, &m_parser, p::_1) + , "Print peer list stats" + ); + m_command_lookup.set_handler( "print_cn" , std::bind(&t_command_parser_executor::print_connections, &m_parser, p::_1) , "Print connections" @@ -96,7 +101,7 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "start_mining" , std::bind(&t_command_parser_executor::start_mining, &m_parser, p::_1) - , "Start mining for specified address, start_mining <addr> [<threads>], default 1 thread" + , "Start mining for specified address, start_mining <addr> [<threads>] [do_background_mining] [ignore_battery], default 1 thread, no background mining" ); m_command_lookup.set_handler( "stop_mining" @@ -238,6 +243,16 @@ t_command_server::t_command_server( , std::bind(&t_command_parser_executor::print_blockchain_dynamic_stats, &m_parser, p::_1) , "Print information about current blockchain dynamic state" ); + m_command_lookup.set_handler( + "update" + , std::bind(&t_command_parser_executor::update, &m_parser, p::_1) + , "subcommands: check (check if an update is available), download (download it is there is), update (not implemented)" + ); + m_command_lookup.set_handler( + "relay_tx" + , std::bind(&t_command_parser_executor::relay_tx, &m_parser, p::_1) + , "Relay a given transaction by its txid" + ); } bool t_command_server::process_command_str(const std::string& cmd) diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h index fb1702aae..476b75141 100644 --- a/src/daemon/command_server.h +++ b/src/daemon/command_server.h @@ -9,7 +9,7 @@ Passing RPC commands: */ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -39,6 +39,8 @@ Passing RPC commands: #pragma once +#include <boost/optional/optional_fwd.hpp> +#include "common/common_fwd.h" #include "console_handler.h" #include "daemon/command_parser_executor.h" @@ -54,7 +56,7 @@ public: t_command_server( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& login , bool is_rpc = true , cryptonote::core_rpc_server* rpc_server = NULL ); diff --git a/src/daemon/core.h b/src/daemon/core.h index 23f7a9f63..9e6ff5e29 100644 --- a/src/daemon/core.h +++ b/src/daemon/core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -45,7 +45,6 @@ public: static void init_options(boost::program_options::options_description & option_spec) { cryptonote::core::init_options(option_spec); - cryptonote::miner::init_options(option_spec); } private: typedef cryptonote::t_cryptonote_protocol_handler<cryptonote::core> t_protocol_raw; diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 287c30cb4..241cb3883 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,6 +33,7 @@ #include "misc_log_ex.h" #include "daemon/daemon.h" +#include "common/password.h" #include "common/util.h" #include "daemon/core.h" #include "daemon/p2p.h" @@ -123,22 +124,24 @@ bool t_daemon::run(bool interactive) return false; mp_internals->rpc.run(); - daemonize::t_command_server* rpc_commands; + std::unique_ptr<daemonize::t_command_server> rpc_commands; if (interactive) { - rpc_commands = new daemonize::t_command_server(0, 0, "", false, mp_internals->rpc.get_server()); + // The first three variables are not used when the fourth is false + rpc_commands.reset(new daemonize::t_command_server(0, 0, boost::none, false, mp_internals->rpc.get_server())); rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this)); } mp_internals->p2p.run(); // blocks until p2p goes down - if (interactive) + if (rpc_commands) { rpc_commands->stop_handling(); } mp_internals->rpc.stop(); + mp_internals->core.get().get_miner().stop(); MGINFO("Node stopped."); return true; } @@ -160,6 +163,7 @@ void t_daemon::stop() { throw std::runtime_error{"Can't stop stopped daemon"}; } + mp_internals->core.get().get_miner().stop(); mp_internals->p2p.stop(); mp_internals->rpc.stop(); mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h index c8fae5c28..2b9f18669 100644 --- a/src/daemon/daemon.h +++ b/src/daemon/daemon.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp index ac5803cfb..6130bfa28 100644 --- a/src/daemon/executor.cpp +++ b/src/daemon/executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -63,6 +63,13 @@ namespace daemonize return t_daemon{vm}; } + bool t_executor::run_non_interactive( + boost::program_options::variables_map const & vm + ) + { + return t_daemon{vm}.run(false); + } + bool t_executor::run_interactive( boost::program_options::variables_map const & vm ) diff --git a/src/daemon/executor.h b/src/daemon/executor.h index a6b47b93d..137e7209c 100644 --- a/src/daemon/executor.h +++ b/src/daemon/executor.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -56,6 +56,10 @@ namespace daemonize boost::program_options::variables_map const & vm ); + bool run_non_interactive( + boost::program_options::variables_map const & vm + ); + bool run_interactive( boost::program_options::variables_map const & vm ); diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index e08065ccd..19dd02171 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,9 +30,10 @@ #include "common/command_line.h" #include "common/scoped_message_writer.h" +#include "common/password.h" #include "common/util.h" #include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_core/miner.h" +#include "cryptonote_basic/miner.h" #include "daemon/command_server.h" #include "daemon/daemon.h" #include "daemon/executor.h" @@ -40,6 +41,7 @@ #include "misc_log_ex.h" #include "p2p/net_node.h" #include "rpc/core_rpc_server.h" +#include "rpc/rpc_args.h" #include "daemon/command_line_args.h" #include "blockchain_db/db_types.h" @@ -166,7 +168,6 @@ int main(int argc, char const * argv[]) // Create data dir if it doesn't exist boost::filesystem::path data_dir = boost::filesystem::absolute( command_line::get_arg(vm, data_dir_arg)); - tools::create_directories_if_necessary(data_dir.string()); // FIXME: not sure on windows implementation default, needs further review //bf::path relative_path_base = daemonizer::get_relative_path_base(vm); @@ -214,19 +215,22 @@ int main(int argc, char const * argv[]) mlog_set_log(command_line::get_arg(vm, daemon_args::arg_log_level).c_str()); } + // after logs initialized + tools::create_directories_if_necessary(data_dir.string()); + // If there are positional options, we're running a daemon command { auto command = command_line::get_arg(vm, daemon_args::arg_command); if (command.size()) { - auto rpc_ip_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_ip); + const cryptonote::rpc_args::descriptors arg{}; + auto rpc_ip_str = command_line::get_arg(vm, arg.rpc_bind_ip); auto rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port); if (testnet_mode) { rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_testnet_rpc_bind_port); } - auto user_agent = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_user_agent); uint32_t rpc_ip; uint16_t rpc_port; @@ -241,7 +245,20 @@ int main(int argc, char const * argv[]) return 1; } - daemonize::t_command_server rpc_commands{rpc_ip, rpc_port, user_agent}; + boost::optional<tools::login> login{}; + if (command_line::has_arg(vm, arg.rpc_login)) + { + login = tools::login::parse( + command_line::get_arg(vm, arg.rpc_login), false, "Daemon client password" + ); + if (!login) + { + std::cerr << "Failed to obtain password" << std::endl; + return 1; + } + } + + daemonize::t_command_server rpc_commands{rpc_ip, rpc_port, std::move(login)}; if (rpc_commands.process_command_vec(command)) { return 0; diff --git a/src/daemon/p2p.h b/src/daemon/p2p.h index f29c2d822..309eb7453 100644 --- a/src/daemon/p2p.h +++ b/src/daemon/p2p.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemon/protocol.h b/src/daemon/protocol.h index 0b0f95988..fc5edbcaa 100644 --- a/src/daemon/protocol.h +++ b/src/daemon/protocol.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -79,7 +79,6 @@ public: m_protocol.deinit(); m_protocol.set_p2p_endpoint(nullptr); MGINFO("Cryptonote protocol stopped successfully"); - tools::success_msg_writer() << "Daemon stopped successfully"; } catch (...) { LOG_ERROR("Failed to stop cryptonote protocol!"); } diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h index 8b0d5808d..0ecfdd120 100644 --- a/src/daemon/rpc.h +++ b/src/daemon/rpc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index f7d85b5ef..31f432918 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,11 +29,12 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "string_tools.h" +#include "common/password.h" #include "common/scoped_message_writer.h" #include "daemon/rpc_command_executor.h" #include "rpc/core_rpc_server_commands_defs.h" #include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_core/hardfork.h" +#include "cryptonote_basic/hardfork.h" #include <boost/format.hpp> #include <ctime> #include <string> @@ -90,12 +91,19 @@ namespace { s = boost::lexical_cast<std::string>(dt/(3600*24)) + " days"; return s + " " + (t > now ? "in the future" : "ago"); } + + std::string make_error(const std::string &base, const std::string &status) + { + if (status == CORE_RPC_STATUS_OK) + return base; + return base + " -- " + status; + } } t_rpc_command_executor::t_rpc_command_executor( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& login , bool is_rpc , cryptonote::core_rpc_server* rpc_server ) @@ -103,7 +111,10 @@ t_rpc_command_executor::t_rpc_command_executor( { if (is_rpc) { - m_rpc_client = new tools::t_rpc_client(ip, port); + boost::optional<epee::net_utils::http::login> http_login{}; + if (login) + http_login.emplace(login->username, login->password.password()); + m_rpc_client = new tools::t_rpc_client(ip, port, std::move(http_login)); } else { @@ -158,6 +169,34 @@ bool t_rpc_command_executor::print_peer_list() { return true; } +bool t_rpc_command_executor::print_peer_list_stats() { + cryptonote::COMMAND_RPC_GET_PEER_LIST::request req; + cryptonote::COMMAND_RPC_GET_PEER_LIST::response res; + + std::string failure_message = "Couldn't retrieve peer list"; + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/get_peer_list", failure_message.c_str())) + { + return false; + } + } + else + { + if (!m_rpc_server->on_get_peer_list(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << failure_message; + return false; + } + } + + tools::msg_writer() + << "White list size: " << res.white_list.size() << "/" << P2P_LOCAL_WHITE_PEERLIST_LIMIT << " (" << res.white_list.size() * 100.0 / P2P_LOCAL_WHITE_PEERLIST_LIMIT << "%)" << std::endl + << "Gray list size: " << res.gray_list.size() << "/" << P2P_LOCAL_GRAY_PEERLIST_LIMIT << " (" << res.gray_list.size() * 100.0 / P2P_LOCAL_GRAY_PEERLIST_LIMIT << "%)"; + + return true; +} + bool t_rpc_command_executor::save_blockchain() { cryptonote::COMMAND_RPC_SAVE_BC::request req; cryptonote::COMMAND_RPC_SAVE_BC::response res; @@ -175,7 +214,7 @@ bool t_rpc_command_executor::save_blockchain() { { if (!m_rpc_server->on_save_bc(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -203,7 +242,7 @@ bool t_rpc_command_executor::show_hash_rate() { { if (!m_rpc_server->on_set_log_hash_rate(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); } } @@ -230,7 +269,7 @@ bool t_rpc_command_executor::hide_hash_rate() { { if (!m_rpc_server->on_set_log_hash_rate(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -257,7 +296,7 @@ bool t_rpc_command_executor::show_difficulty() { { if (!m_rpc_server->on_get_info(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message.c_str(), res.status); return true; } } @@ -341,12 +380,12 @@ bool t_rpc_command_executor::show_status() { { if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, ires.status); return true; } if (!m_rpc_server->on_hard_fork_info(hfreq, hfres, error_resp) || hfres.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, hfres.status); return true; } if (!m_rpc_server->on_mining_status(mreq, mres)) @@ -361,19 +400,19 @@ bool t_rpc_command_executor::show_status() { } else if (mres.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, mres.status); return true; } } std::time_t uptime = std::time(nullptr) - ires.start_time; - tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u+%u connections, uptime %ud %uh %um %us") + tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us") % (unsigned long long)ires.height % (unsigned long long)(ires.target_height >= ires.height ? ires.target_height : ires.height) % get_sync_percentage(ires) % (ires.testnet ? "testnet" : "mainnet") - % (mining_busy ? "syncing" : mres.active ? "mining at " + get_mining_speed(mres.speed) : "not mining") + % (mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining") % get_mining_speed(ires.difficulty / ires.target) % (unsigned)hfres.version % get_fork_extra_info(hfres.earliest_height, ires.height, ires.target) @@ -407,7 +446,7 @@ bool t_rpc_command_executor::print_connections() { { if (!m_rpc_server->on_get_connections(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -472,7 +511,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u { if (!m_rpc_server->on_get_block_headers_range(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -512,7 +551,7 @@ bool t_rpc_command_executor::set_log_level(int8_t level) { { if (!m_rpc_server->on_set_log_level(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -540,7 +579,7 @@ bool t_rpc_command_executor::set_log_categories(const std::string &categories) { { if (!m_rpc_server->on_set_log_categories(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -567,7 +606,7 @@ bool t_rpc_command_executor::print_height() { { if (!m_rpc_server->on_get_height(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -597,7 +636,7 @@ bool t_rpc_command_executor::print_block_by_hash(crypto::hash block_hash) { { if (!m_rpc_server->on_get_block(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -628,7 +667,7 @@ bool t_rpc_command_executor::print_block_by_height(uint64_t height) { { if (!m_rpc_server->on_get_block(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -657,7 +696,7 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash) { { if (!m_rpc_server->on_get_transactions(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -720,7 +759,7 @@ bool t_rpc_command_executor::is_key_image_spent(const crypto::key_image &ki) { { if (!m_rpc_server->on_is_key_image_spent(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -755,7 +794,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() { { if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -774,6 +813,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() { << tx_info.tx_json << std::endl << "blob_size: " << tx_info.blob_size << std::endl << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl + << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl @@ -838,7 +878,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() { { if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -855,6 +895,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() { tools::msg_writer() << "id: " << tx_info.id_hash << std::endl << "blob_size: " << tx_info.blob_size << std::endl << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl + << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl @@ -886,7 +927,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { { if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -919,18 +960,20 @@ bool t_rpc_command_executor::print_transaction_pool_stats() { size_t avg_bytes = n_transactions ? bytes / n_transactions : 0; tools::msg_writer() << n_transactions << " tx(es), " << bytes << " bytes total (min " << min_bytes << ", max " << max_bytes << ", avg " << avg_bytes << ")" << std::endl - << "fees " << cryptonote::print_money(fee) << " (avg " << cryptonote::print_money(n_transactions ? fee / n_transactions : 0) << " per tx)" << std::endl + << "fees " << cryptonote::print_money(fee) << " (avg " << cryptonote::print_money(n_transactions ? fee / n_transactions : 0) << " per tx" << ", " << cryptonote::print_money(bytes ? fee / bytes : 0) << " per byte )" << std::endl << n_not_relayed << " not relayed, " << n_failing << " failing, " << n_10m << " older than 10 minutes (oldest " << (oldest == 0 ? "-" : get_human_time_ago(oldest, now)) << ")" << std::endl; return true; } -bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet) { +bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining, bool ignore_battery) { cryptonote::COMMAND_RPC_START_MINING::request req; cryptonote::COMMAND_RPC_START_MINING::response res; req.miner_address = cryptonote::get_account_address_as_str(testnet, address); req.threads_count = num_threads; - + req.do_background_mining = do_background_mining; + req.ignore_battery = ignore_battery; + std::string fail_message = "Mining did not start"; if (m_is_rpc) @@ -944,7 +987,7 @@ bool t_rpc_command_executor::start_mining(cryptonote::account_public_address add { if (!m_rpc_server->on_start_mining(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -969,7 +1012,7 @@ bool t_rpc_command_executor::stop_mining() { { if (!m_rpc_server->on_stop_mining(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1010,7 +1053,7 @@ bool t_rpc_command_executor::stop_daemon() { if (!m_rpc_server->on_stop_daemon(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1108,7 +1151,7 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) { if (!m_rpc_server->on_out_peers(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1136,7 +1179,7 @@ bool t_rpc_command_executor::start_save_graph() { if (!m_rpc_server->on_start_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1162,7 +1205,7 @@ bool t_rpc_command_executor::stop_save_graph() { if (!m_rpc_server->on_stop_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1189,7 +1232,7 @@ bool t_rpc_command_executor::hard_fork_info(uint8_t version) { if (!m_rpc_server->on_hard_fork_info(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1220,7 +1263,7 @@ bool t_rpc_command_executor::print_bans() { if (!m_rpc_server->on_get_bans(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1262,7 +1305,7 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds) { if (!m_rpc_server->on_set_bans(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1298,7 +1341,7 @@ bool t_rpc_command_executor::unban(const std::string &ip) { if (!m_rpc_server->on_set_bans(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1325,9 +1368,9 @@ bool t_rpc_command_executor::flush_txpool(const std::string &txid) } else { - if (!m_rpc_server->on_flush_txpool(req, res, error_resp)) + if (!m_rpc_server->on_flush_txpool(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1356,9 +1399,9 @@ bool t_rpc_command_executor::output_histogram(uint64_t min_count, uint64_t max_c } else { - if (!m_rpc_server->on_get_output_histogram(req, res, error_resp)) + if (!m_rpc_server->on_get_output_histogram(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1393,9 +1436,9 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou } else { - if (!m_rpc_server->on_get_coinbase_tx_sum(req, res, error_resp)) + if (!m_rpc_server->on_get_coinbase_tx_sum(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1410,6 +1453,8 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou bool t_rpc_command_executor::alt_chain_info() { + cryptonote::COMMAND_RPC_GET_INFO::request ireq; + cryptonote::COMMAND_RPC_GET_INFO::response ires; cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::request req; cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::response res; epee::json_rpc::error error_resp; @@ -1418,6 +1463,10 @@ bool t_rpc_command_executor::alt_chain_info() if (m_is_rpc) { + if (!m_rpc_client->rpc_request(ireq, ires, "/getinfo", fail_message.c_str())) + { + return true; + } if (!m_rpc_client->json_rpc_request(req, res, "get_alternate_chains", fail_message.c_str())) { return true; @@ -1425,9 +1474,14 @@ bool t_rpc_command_executor::alt_chain_info() } else { + if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, ires.status); + return true; + } if (!m_rpc_server->on_get_alternate_chains(req, res, error_resp)) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, res.status); return true; } } @@ -1435,8 +1489,9 @@ bool t_rpc_command_executor::alt_chain_info() tools::msg_writer() << boost::lexical_cast<std::string>(res.chains.size()) << " alternate chains found:"; for (const auto chain: res.chains) { - tools::msg_writer() << chain.length << " blocks long, branching at height " << (chain.height - chain.length + 1) - << ", difficulty " << chain.difficulty << ": " << chain.block_hash; + uint64_t start_height = (chain.height - chain.length + 1); + tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1) + << " deep), diff " << chain.difficulty << ": " << chain.block_hash; } return true; } @@ -1460,7 +1515,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) { return true; } - if (!m_rpc_client->rpc_request(fereq, feres, "/get_fee_estimate", fail_message.c_str())) + if (!m_rpc_client->json_rpc_request(fereq, feres, "get_fee_estimate", fail_message.c_str())) { return true; } @@ -1469,12 +1524,12 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) { if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, ires.status); return true; } if (!m_rpc_server->on_get_per_kb_fee_estimate(fereq, feres, error_resp) || feres.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, feres.status); return true; } } @@ -1491,7 +1546,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) bhreq.end_height = ires.height - 1; if (m_is_rpc) { - if (!m_rpc_client->rpc_request(bhreq, bhres, "/getblockheadersrange", fail_message.c_str())) + if (!m_rpc_client->json_rpc_request(bhreq, bhres, "getblockheadersrange", fail_message.c_str())) { return true; } @@ -1500,7 +1555,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) { if (!m_rpc_server->on_get_block_headers_range(bhreq, bhres, error_resp) || bhres.status != CORE_RPC_STATUS_OK) { - tools::fail_msg_writer() << fail_message.c_str(); + tools::fail_msg_writer() << make_error(fail_message, bhres.status); return true; } } @@ -1546,4 +1601,79 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) return true; } +bool t_rpc_command_executor::update(const std::string &command) +{ + cryptonote::COMMAND_RPC_UPDATE::request req; + cryptonote::COMMAND_RPC_UPDATE::response res; + epee::json_rpc::error error_resp; + + std::string fail_message = "Problem fetching info"; + + req.command = command; + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/update", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_update(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + if (!res.update) + { + tools::msg_writer() << "No update available"; + return true; + } + + tools::msg_writer() << "Update available: v" << res.version << ": " << res.user_uri << ", hash " << res.hash; + if (command == "check") + return true; + + if (!res.path.empty()) + tools::msg_writer() << "Update downloaded to: " << res.path; + else + tools::msg_writer() << "Update download failed: " << res.status; + if (command == "download") + return true; + + tools::msg_writer() << "'update' not implemented yet"; + + return true; +} + +bool t_rpc_command_executor::relay_tx(const std::string &txid) +{ + cryptonote::COMMAND_RPC_RELAY_TX::request req; + cryptonote::COMMAND_RPC_RELAY_TX::response res; + std::string fail_message = "Unsuccessful"; + epee::json_rpc::error error_resp; + + req.txids.push_back(txid); + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "relay_tx", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_relay_tx(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + return true; +} + }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index afcd99d32..3f551bd14 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -6,7 +6,7 @@ */ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -38,6 +38,9 @@ #pragma once +#include <boost/optional/optional_fwd.hpp> + +#include "common/common_fwd.h" #include "common/rpc_client.h" #include "misc_log_ex.h" #include "cryptonote_core/cryptonote_core.h" @@ -60,7 +63,7 @@ public: t_rpc_command_executor( uint32_t ip , uint16_t port - , const std::string &user_agent + , const boost::optional<tools::login>& user , bool is_rpc = true , cryptonote::core_rpc_server* rpc_server = NULL ); @@ -69,6 +72,8 @@ public: bool print_peer_list(); + bool print_peer_list_stats(); + bool save_blockchain(); bool show_hash_rate(); @@ -103,7 +108,7 @@ public: bool print_transaction_pool_stats(); - bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet); + bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining = false, bool ignore_battery = false); bool stop_mining(); @@ -146,6 +151,10 @@ public: bool alt_chain_info(); bool print_blockchain_dynamic_stats(uint64_t nblocks); + + bool update(const std::string &command); + + bool relay_tx(const std::string &txid); }; } // namespace daemonize diff --git a/src/daemonizer/CMakeLists.txt b/src/daemonizer/CMakeLists.txt index 964c8cc6f..c8cb1b445 100644 --- a/src/daemonizer/CMakeLists.txt +++ b/src/daemonizer/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/src/daemonizer/daemonizer.h b/src/daemonizer/daemonizer.h index 3e30d85ee..5f53d062b 100644 --- a/src/daemonizer/daemonizer.h +++ b/src/daemonizer/daemonizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl index 926f0a3ac..f8be15dda 100644 --- a/src/daemonizer/posix_daemonizer.inl +++ b/src/daemonizer/posix_daemonizer.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -43,6 +43,10 @@ namespace daemonizer "detach" , "Run as daemon" }; + const command_line::arg_descriptor<bool> arg_non_interactive = { + "non-interactive" + , "Run non-interactive" + }; } inline void init_options( @@ -51,6 +55,7 @@ namespace daemonizer ) { command_line::add_arg(normal_options, arg_detach); + command_line::add_arg(normal_options, arg_non_interactive); } inline boost::filesystem::path get_default_data_dir() @@ -79,6 +84,10 @@ namespace daemonizer auto daemon = executor.create_daemon(vm); return daemon.run(); } + else if (command_line::has_arg(vm, arg_non_interactive)) + { + return executor.run_non_interactive(vm); + } else { //LOG_PRINT_L0("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL); diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h index ef627a43e..459417d25 100644 --- a/src/daemonizer/posix_fork.h +++ b/src/daemonizer/posix_fork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl index d8023944e..012cd1e67 100644 --- a/src/daemonizer/windows_daemonizer.inl +++ b/src/daemonizer/windows_daemonizer.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp index f8cc0c6c7..d540f5bf8 100644 --- a/src/daemonizer/windows_service.cpp +++ b/src/daemonizer/windows_service.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service.h b/src/daemonizer/windows_service.h index 2712d13a3..070434b04 100644 --- a/src/daemonizer/windows_service.h +++ b/src/daemonizer/windows_service.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service_runner.h b/src/daemonizer/windows_service_runner.h index f4258a215..528d13a53 100644 --- a/src/daemonizer/windows_service_runner.h +++ b/src/daemonizer/windows_service_runner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt index 936c43b99..4db19d195 100644 --- a/src/mnemonics/CMakeLists.txt +++ b/src/mnemonics/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -33,7 +33,10 @@ set(mnemonics_headers) set(mnemonics_private_headers electrum-words.h + chinese_simplified.h english.h + dutch.h + french.h german.h italian.h japanese.h @@ -51,5 +54,7 @@ monero_add_library(mnemonics ${mnemonics_headers} ${mnemonics_private_headers}) target_link_libraries(mnemonics + PUBLIC + ${Boost_SYSTEM_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h new file mode 100644 index 000000000..413186733 --- /dev/null +++ b/src/mnemonics/chinese_simplified.h @@ -0,0 +1,1709 @@ +// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/*!
+ * \file chinese_simplified.h
+ *
+ * \brief Simplified Chinese word list and map.
+ */
+
+#ifndef CHINESE_SIMPLIFIED_H
+#define CHINESE_SIMPLIFIED_H
+
+#include <vector>
+#include <unordered_map>
+#include "language_base.h"
+#include <string>
+
+/*!
+ * \namespace Language
+ * \brief Mnemonic language related namespace.
+ */
+namespace Language
+{
+ class Chinese_Simplified: public Base
+ {
+ public:
+ Chinese_Simplified(): Base("Chinese (Simplified)", std::vector<std::string>({
+ "的",
+ "一",
+ "是",
+ "在",
+ "不",
+ "了",
+ "有",
+ "和",
+ "人",
+ "这",
+ "中",
+ "大",
+ "为",
+ "上",
+ "个",
+ "国",
+ "我",
+ "以",
+ "要",
+ "他",
+ "时",
+ "来",
+ "用",
+ "们",
+ "生",
+ "到",
+ "作",
+ "地",
+ "于",
+ "出",
+ "就",
+ "分",
+ "对",
+ "成",
+ "会",
+ "可",
+ "主",
+ "发",
+ "年",
+ "动",
+ "同",
+ "工",
+ "也",
+ "能",
+ "下",
+ "过",
+ "子",
+ "说",
+ "产",
+ "种",
+ "面",
+ "而",
+ "方",
+ "后",
+ "多",
+ "定",
+ "行",
+ "学",
+ "法",
+ "所",
+ "民",
+ "得",
+ "经",
+ "十",
+ "三",
+ "之",
+ "进",
+ "着",
+ "等",
+ "部",
+ "度",
+ "家",
+ "电",
+ "力",
+ "里",
+ "如",
+ "水",
+ "化",
+ "高",
+ "自",
+ "二",
+ "理",
+ "起",
+ "小",
+ "物",
+ "现",
+ "实",
+ "加",
+ "量",
+ "都",
+ "两",
+ "体",
+ "制",
+ "机",
+ "当",
+ "使",
+ "点",
+ "从",
+ "业",
+ "本",
+ "去",
+ "把",
+ "性",
+ "好",
+ "应",
+ "开",
+ "它",
+ "合",
+ "还",
+ "因",
+ "由",
+ "其",
+ "些",
+ "然",
+ "前",
+ "外",
+ "天",
+ "政",
+ "四",
+ "日",
+ "那",
+ "社",
+ "义",
+ "事",
+ "平",
+ "形",
+ "相",
+ "全",
+ "表",
+ "间",
+ "样",
+ "与",
+ "关",
+ "各",
+ "重",
+ "新",
+ "线",
+ "内",
+ "数",
+ "正",
+ "心",
+ "反",
+ "你",
+ "明",
+ "看",
+ "原",
+ "又",
+ "么",
+ "利",
+ "比",
+ "或",
+ "但",
+ "质",
+ "气",
+ "第",
+ "向",
+ "道",
+ "命",
+ "此",
+ "变",
+ "条",
+ "只",
+ "没",
+ "结",
+ "解",
+ "问",
+ "意",
+ "建",
+ "月",
+ "公",
+ "无",
+ "系",
+ "军",
+ "很",
+ "情",
+ "者",
+ "最",
+ "立",
+ "代",
+ "想",
+ "已",
+ "通",
+ "并",
+ "提",
+ "直",
+ "题",
+ "党",
+ "程",
+ "展",
+ "五",
+ "果",
+ "料",
+ "象",
+ "员",
+ "革",
+ "位",
+ "入",
+ "常",
+ "文",
+ "总",
+ "次",
+ "品",
+ "式",
+ "活",
+ "设",
+ "及",
+ "管",
+ "特",
+ "件",
+ "长",
+ "求",
+ "老",
+ "头",
+ "基",
+ "资",
+ "边",
+ "流",
+ "路",
+ "级",
+ "少",
+ "图",
+ "山",
+ "统",
+ "接",
+ "知",
+ "较",
+ "将",
+ "组",
+ "见",
+ "计",
+ "别",
+ "她",
+ "手",
+ "角",
+ "期",
+ "根",
+ "论",
+ "运",
+ "农",
+ "指",
+ "几",
+ "九",
+ "区",
+ "强",
+ "放",
+ "决",
+ "西",
+ "被",
+ "干",
+ "做",
+ "必",
+ "战",
+ "先",
+ "回",
+ "则",
+ "任",
+ "取",
+ "据",
+ "处",
+ "队",
+ "南",
+ "给",
+ "色",
+ "光",
+ "门",
+ "即",
+ "保",
+ "治",
+ "北",
+ "造",
+ "百",
+ "规",
+ "热",
+ "领",
+ "七",
+ "海",
+ "口",
+ "东",
+ "导",
+ "器",
+ "压",
+ "志",
+ "世",
+ "金",
+ "增",
+ "争",
+ "济",
+ "阶",
+ "油",
+ "思",
+ "术",
+ "极",
+ "交",
+ "受",
+ "联",
+ "什",
+ "认",
+ "六",
+ "共",
+ "权",
+ "收",
+ "证",
+ "改",
+ "清",
+ "美",
+ "再",
+ "采",
+ "转",
+ "更",
+ "单",
+ "风",
+ "切",
+ "打",
+ "白",
+ "教",
+ "速",
+ "花",
+ "带",
+ "安",
+ "场",
+ "身",
+ "车",
+ "例",
+ "真",
+ "务",
+ "具",
+ "万",
+ "每",
+ "目",
+ "至",
+ "达",
+ "走",
+ "积",
+ "示",
+ "议",
+ "声",
+ "报",
+ "斗",
+ "完",
+ "类",
+ "八",
+ "离",
+ "华",
+ "名",
+ "确",
+ "才",
+ "科",
+ "张",
+ "信",
+ "马",
+ "节",
+ "话",
+ "米",
+ "整",
+ "空",
+ "元",
+ "况",
+ "今",
+ "集",
+ "温",
+ "传",
+ "土",
+ "许",
+ "步",
+ "群",
+ "广",
+ "石",
+ "记",
+ "需",
+ "段",
+ "研",
+ "界",
+ "拉",
+ "林",
+ "律",
+ "叫",
+ "且",
+ "究",
+ "观",
+ "越",
+ "织",
+ "装",
+ "影",
+ "算",
+ "低",
+ "持",
+ "音",
+ "众",
+ "书",
+ "布",
+ "复",
+ "容",
+ "儿",
+ "须",
+ "际",
+ "商",
+ "非",
+ "验",
+ "连",
+ "断",
+ "深",
+ "难",
+ "近",
+ "矿",
+ "千",
+ "周",
+ "委",
+ "素",
+ "技",
+ "备",
+ "半",
+ "办",
+ "青",
+ "省",
+ "列",
+ "习",
+ "响",
+ "约",
+ "支",
+ "般",
+ "史",
+ "感",
+ "劳",
+ "便",
+ "团",
+ "往",
+ "酸",
+ "历",
+ "市",
+ "克",
+ "何",
+ "除",
+ "消",
+ "构",
+ "府",
+ "称",
+ "太",
+ "准",
+ "精",
+ "值",
+ "号",
+ "率",
+ "族",
+ "维",
+ "划",
+ "选",
+ "标",
+ "写",
+ "存",
+ "候",
+ "毛",
+ "亲",
+ "快",
+ "效",
+ "斯",
+ "院",
+ "查",
+ "江",
+ "型",
+ "眼",
+ "王",
+ "按",
+ "格",
+ "养",
+ "易",
+ "置",
+ "派",
+ "层",
+ "片",
+ "始",
+ "却",
+ "专",
+ "状",
+ "育",
+ "厂",
+ "京",
+ "识",
+ "适",
+ "属",
+ "圆",
+ "包",
+ "火",
+ "住",
+ "调",
+ "满",
+ "县",
+ "局",
+ "照",
+ "参",
+ "红",
+ "细",
+ "引",
+ "听",
+ "该",
+ "铁",
+ "价",
+ "严",
+ "首",
+ "底",
+ "液",
+ "官",
+ "德",
+ "随",
+ "病",
+ "苏",
+ "失",
+ "尔",
+ "死",
+ "讲",
+ "配",
+ "女",
+ "黄",
+ "推",
+ "显",
+ "谈",
+ "罪",
+ "神",
+ "艺",
+ "呢",
+ "席",
+ "含",
+ "企",
+ "望",
+ "密",
+ "批",
+ "营",
+ "项",
+ "防",
+ "举",
+ "球",
+ "英",
+ "氧",
+ "势",
+ "告",
+ "李",
+ "台",
+ "落",
+ "木",
+ "帮",
+ "轮",
+ "破",
+ "亚",
+ "师",
+ "围",
+ "注",
+ "远",
+ "字",
+ "材",
+ "排",
+ "供",
+ "河",
+ "态",
+ "封",
+ "另",
+ "施",
+ "减",
+ "树",
+ "溶",
+ "怎",
+ "止",
+ "案",
+ "言",
+ "士",
+ "均",
+ "武",
+ "固",
+ "叶",
+ "鱼",
+ "波",
+ "视",
+ "仅",
+ "费",
+ "紧",
+ "爱",
+ "左",
+ "章",
+ "早",
+ "朝",
+ "害",
+ "续",
+ "轻",
+ "服",
+ "试",
+ "食",
+ "充",
+ "兵",
+ "源",
+ "判",
+ "护",
+ "司",
+ "足",
+ "某",
+ "练",
+ "差",
+ "致",
+ "板",
+ "田",
+ "降",
+ "黑",
+ "犯",
+ "负",
+ "击",
+ "范",
+ "继",
+ "兴",
+ "似",
+ "余",
+ "坚",
+ "曲",
+ "输",
+ "修",
+ "故",
+ "城",
+ "夫",
+ "够",
+ "送",
+ "笔",
+ "船",
+ "占",
+ "右",
+ "财",
+ "吃",
+ "富",
+ "春",
+ "职",
+ "觉",
+ "汉",
+ "画",
+ "功",
+ "巴",
+ "跟",
+ "虽",
+ "杂",
+ "飞",
+ "检",
+ "吸",
+ "助",
+ "升",
+ "阳",
+ "互",
+ "初",
+ "创",
+ "抗",
+ "考",
+ "投",
+ "坏",
+ "策",
+ "古",
+ "径",
+ "换",
+ "未",
+ "跑",
+ "留",
+ "钢",
+ "曾",
+ "端",
+ "责",
+ "站",
+ "简",
+ "述",
+ "钱",
+ "副",
+ "尽",
+ "帝",
+ "射",
+ "草",
+ "冲",
+ "承",
+ "独",
+ "令",
+ "限",
+ "阿",
+ "宣",
+ "环",
+ "双",
+ "请",
+ "超",
+ "微",
+ "让",
+ "控",
+ "州",
+ "良",
+ "轴",
+ "找",
+ "否",
+ "纪",
+ "益",
+ "依",
+ "优",
+ "顶",
+ "础",
+ "载",
+ "倒",
+ "房",
+ "突",
+ "坐",
+ "粉",
+ "敌",
+ "略",
+ "客",
+ "袁",
+ "冷",
+ "胜",
+ "绝",
+ "析",
+ "块",
+ "剂",
+ "测",
+ "丝",
+ "协",
+ "诉",
+ "念",
+ "陈",
+ "仍",
+ "罗",
+ "盐",
+ "友",
+ "洋",
+ "错",
+ "苦",
+ "夜",
+ "刑",
+ "移",
+ "频",
+ "逐",
+ "靠",
+ "混",
+ "母",
+ "短",
+ "皮",
+ "终",
+ "聚",
+ "汽",
+ "村",
+ "云",
+ "哪",
+ "既",
+ "距",
+ "卫",
+ "停",
+ "烈",
+ "央",
+ "察",
+ "烧",
+ "迅",
+ "境",
+ "若",
+ "印",
+ "洲",
+ "刻",
+ "括",
+ "激",
+ "孔",
+ "搞",
+ "甚",
+ "室",
+ "待",
+ "核",
+ "校",
+ "散",
+ "侵",
+ "吧",
+ "甲",
+ "游",
+ "久",
+ "菜",
+ "味",
+ "旧",
+ "模",
+ "湖",
+ "货",
+ "损",
+ "预",
+ "阻",
+ "毫",
+ "普",
+ "稳",
+ "乙",
+ "妈",
+ "植",
+ "息",
+ "扩",
+ "银",
+ "语",
+ "挥",
+ "酒",
+ "守",
+ "拿",
+ "序",
+ "纸",
+ "医",
+ "缺",
+ "雨",
+ "吗",
+ "针",
+ "刘",
+ "啊",
+ "急",
+ "唱",
+ "误",
+ "训",
+ "愿",
+ "审",
+ "附",
+ "获",
+ "茶",
+ "鲜",
+ "粮",
+ "斤",
+ "孩",
+ "脱",
+ "硫",
+ "肥",
+ "善",
+ "龙",
+ "演",
+ "父",
+ "渐",
+ "血",
+ "欢",
+ "械",
+ "掌",
+ "歌",
+ "沙",
+ "刚",
+ "攻",
+ "谓",
+ "盾",
+ "讨",
+ "晚",
+ "粒",
+ "乱",
+ "燃",
+ "矛",
+ "乎",
+ "杀",
+ "药",
+ "宁",
+ "鲁",
+ "贵",
+ "钟",
+ "煤",
+ "读",
+ "班",
+ "伯",
+ "香",
+ "介",
+ "迫",
+ "句",
+ "丰",
+ "培",
+ "握",
+ "兰",
+ "担",
+ "弦",
+ "蛋",
+ "沉",
+ "假",
+ "穿",
+ "执",
+ "答",
+ "乐",
+ "谁",
+ "顺",
+ "烟",
+ "缩",
+ "征",
+ "脸",
+ "喜",
+ "松",
+ "脚",
+ "困",
+ "异",
+ "免",
+ "背",
+ "星",
+ "福",
+ "买",
+ "染",
+ "井",
+ "概",
+ "慢",
+ "怕",
+ "磁",
+ "倍",
+ "祖",
+ "皇",
+ "促",
+ "静",
+ "补",
+ "评",
+ "翻",
+ "肉",
+ "践",
+ "尼",
+ "衣",
+ "宽",
+ "扬",
+ "棉",
+ "希",
+ "伤",
+ "操",
+ "垂",
+ "秋",
+ "宜",
+ "氢",
+ "套",
+ "督",
+ "振",
+ "架",
+ "亮",
+ "末",
+ "宪",
+ "庆",
+ "编",
+ "牛",
+ "触",
+ "映",
+ "雷",
+ "销",
+ "诗",
+ "座",
+ "居",
+ "抓",
+ "裂",
+ "胞",
+ "呼",
+ "娘",
+ "景",
+ "威",
+ "绿",
+ "晶",
+ "厚",
+ "盟",
+ "衡",
+ "鸡",
+ "孙",
+ "延",
+ "危",
+ "胶",
+ "屋",
+ "乡",
+ "临",
+ "陆",
+ "顾",
+ "掉",
+ "呀",
+ "灯",
+ "岁",
+ "措",
+ "束",
+ "耐",
+ "剧",
+ "玉",
+ "赵",
+ "跳",
+ "哥",
+ "季",
+ "课",
+ "凯",
+ "胡",
+ "额",
+ "款",
+ "绍",
+ "卷",
+ "齐",
+ "伟",
+ "蒸",
+ "殖",
+ "永",
+ "宗",
+ "苗",
+ "川",
+ "炉",
+ "岩",
+ "弱",
+ "零",
+ "杨",
+ "奏",
+ "沿",
+ "露",
+ "杆",
+ "探",
+ "滑",
+ "镇",
+ "饭",
+ "浓",
+ "航",
+ "怀",
+ "赶",
+ "库",
+ "夺",
+ "伊",
+ "灵",
+ "税",
+ "途",
+ "灭",
+ "赛",
+ "归",
+ "召",
+ "鼓",
+ "播",
+ "盘",
+ "裁",
+ "险",
+ "康",
+ "唯",
+ "录",
+ "菌",
+ "纯",
+ "借",
+ "糖",
+ "盖",
+ "横",
+ "符",
+ "私",
+ "努",
+ "堂",
+ "域",
+ "枪",
+ "润",
+ "幅",
+ "哈",
+ "竟",
+ "熟",
+ "虫",
+ "泽",
+ "脑",
+ "壤",
+ "碳",
+ "欧",
+ "遍",
+ "侧",
+ "寨",
+ "敢",
+ "彻",
+ "虑",
+ "斜",
+ "薄",
+ "庭",
+ "纳",
+ "弹",
+ "饲",
+ "伸",
+ "折",
+ "麦",
+ "湿",
+ "暗",
+ "荷",
+ "瓦",
+ "塞",
+ "床",
+ "筑",
+ "恶",
+ "户",
+ "访",
+ "塔",
+ "奇",
+ "透",
+ "梁",
+ "刀",
+ "旋",
+ "迹",
+ "卡",
+ "氯",
+ "遇",
+ "份",
+ "毒",
+ "泥",
+ "退",
+ "洗",
+ "摆",
+ "灰",
+ "彩",
+ "卖",
+ "耗",
+ "夏",
+ "择",
+ "忙",
+ "铜",
+ "献",
+ "硬",
+ "予",
+ "繁",
+ "圈",
+ "雪",
+ "函",
+ "亦",
+ "抽",
+ "篇",
+ "阵",
+ "阴",
+ "丁",
+ "尺",
+ "追",
+ "堆",
+ "雄",
+ "迎",
+ "泛",
+ "爸",
+ "楼",
+ "避",
+ "谋",
+ "吨",
+ "野",
+ "猪",
+ "旗",
+ "累",
+ "偏",
+ "典",
+ "馆",
+ "索",
+ "秦",
+ "脂",
+ "潮",
+ "爷",
+ "豆",
+ "忽",
+ "托",
+ "惊",
+ "塑",
+ "遗",
+ "愈",
+ "朱",
+ "替",
+ "纤",
+ "粗",
+ "倾",
+ "尚",
+ "痛",
+ "楚",
+ "谢",
+ "奋",
+ "购",
+ "磨",
+ "君",
+ "池",
+ "旁",
+ "碎",
+ "骨",
+ "监",
+ "捕",
+ "弟",
+ "暴",
+ "割",
+ "贯",
+ "殊",
+ "释",
+ "词",
+ "亡",
+ "壁",
+ "顿",
+ "宝",
+ "午",
+ "尘",
+ "闻",
+ "揭",
+ "炮",
+ "残",
+ "冬",
+ "桥",
+ "妇",
+ "警",
+ "综",
+ "招",
+ "吴",
+ "付",
+ "浮",
+ "遭",
+ "徐",
+ "您",
+ "摇",
+ "谷",
+ "赞",
+ "箱",
+ "隔",
+ "订",
+ "男",
+ "吹",
+ "园",
+ "纷",
+ "唐",
+ "败",
+ "宋",
+ "玻",
+ "巨",
+ "耕",
+ "坦",
+ "荣",
+ "闭",
+ "湾",
+ "键",
+ "凡",
+ "驻",
+ "锅",
+ "救",
+ "恩",
+ "剥",
+ "凝",
+ "碱",
+ "齿",
+ "截",
+ "炼",
+ "麻",
+ "纺",
+ "禁",
+ "废",
+ "盛",
+ "版",
+ "缓",
+ "净",
+ "睛",
+ "昌",
+ "婚",
+ "涉",
+ "筒",
+ "嘴",
+ "插",
+ "岸",
+ "朗",
+ "庄",
+ "街",
+ "藏",
+ "姑",
+ "贸",
+ "腐",
+ "奴",
+ "啦",
+ "惯",
+ "乘",
+ "伙",
+ "恢",
+ "匀",
+ "纱",
+ "扎",
+ "辩",
+ "耳",
+ "彪",
+ "臣",
+ "亿",
+ "璃",
+ "抵",
+ "脉",
+ "秀",
+ "萨",
+ "俄",
+ "网",
+ "舞",
+ "店",
+ "喷",
+ "纵",
+ "寸",
+ "汗",
+ "挂",
+ "洪",
+ "贺",
+ "闪",
+ "柬",
+ "爆",
+ "烯",
+ "津",
+ "稻",
+ "墙",
+ "软",
+ "勇",
+ "像",
+ "滚",
+ "厘",
+ "蒙",
+ "芳",
+ "肯",
+ "坡",
+ "柱",
+ "荡",
+ "腿",
+ "仪",
+ "旅",
+ "尾",
+ "轧",
+ "冰",
+ "贡",
+ "登",
+ "黎",
+ "削",
+ "钻",
+ "勒",
+ "逃",
+ "障",
+ "氨",
+ "郭",
+ "峰",
+ "币",
+ "港",
+ "伏",
+ "轨",
+ "亩",
+ "毕",
+ "擦",
+ "莫",
+ "刺",
+ "浪",
+ "秘",
+ "援",
+ "株",
+ "健",
+ "售",
+ "股",
+ "岛",
+ "甘",
+ "泡",
+ "睡",
+ "童",
+ "铸",
+ "汤",
+ "阀",
+ "休",
+ "汇",
+ "舍",
+ "牧",
+ "绕",
+ "炸",
+ "哲",
+ "磷",
+ "绩",
+ "朋",
+ "淡",
+ "尖",
+ "启",
+ "陷",
+ "柴",
+ "呈",
+ "徒",
+ "颜",
+ "泪",
+ "稍",
+ "忘",
+ "泵",
+ "蓝",
+ "拖",
+ "洞",
+ "授",
+ "镜",
+ "辛",
+ "壮",
+ "锋",
+ "贫",
+ "虚",
+ "弯",
+ "摩",
+ "泰",
+ "幼",
+ "廷",
+ "尊",
+ "窗",
+ "纲",
+ "弄",
+ "隶",
+ "疑",
+ "氏",
+ "宫",
+ "姐",
+ "震",
+ "瑞",
+ "怪",
+ "尤",
+ "琴",
+ "循",
+ "描",
+ "膜",
+ "违",
+ "夹",
+ "腰",
+ "缘",
+ "珠",
+ "穷",
+ "森",
+ "枝",
+ "竹",
+ "沟",
+ "催",
+ "绳",
+ "忆",
+ "邦",
+ "剩",
+ "幸",
+ "浆",
+ "栏",
+ "拥",
+ "牙",
+ "贮",
+ "礼",
+ "滤",
+ "钠",
+ "纹",
+ "罢",
+ "拍",
+ "咱",
+ "喊",
+ "袖",
+ "埃",
+ "勤",
+ "罚",
+ "焦",
+ "潜",
+ "伍",
+ "墨",
+ "欲",
+ "缝",
+ "姓",
+ "刊",
+ "饱",
+ "仿",
+ "奖",
+ "铝",
+ "鬼",
+ "丽",
+ "跨",
+ "默",
+ "挖",
+ "链",
+ "扫",
+ "喝",
+ "袋",
+ "炭",
+ "污",
+ "幕",
+ "诸",
+ "弧",
+ "励",
+ "梅",
+ "奶",
+ "洁",
+ "灾",
+ "舟",
+ "鉴",
+ "苯",
+ "讼",
+ "抱",
+ "毁",
+ "懂",
+ "寒",
+ "智",
+ "埔",
+ "寄",
+ "届",
+ "跃",
+ "渡",
+ "挑",
+ "丹",
+ "艰",
+ "贝",
+ "碰",
+ "拔",
+ "爹",
+ "戴",
+ "码",
+ "梦",
+ "芽",
+ "熔",
+ "赤",
+ "渔",
+ "哭",
+ "敬",
+ "颗",
+ "奔",
+ "铅",
+ "仲",
+ "虎",
+ "稀",
+ "妹",
+ "乏",
+ "珍",
+ "申",
+ "桌",
+ "遵",
+ "允",
+ "隆",
+ "螺",
+ "仓",
+ "魏",
+ "锐",
+ "晓",
+ "氮",
+ "兼",
+ "隐",
+ "碍",
+ "赫",
+ "拨",
+ "忠",
+ "肃",
+ "缸",
+ "牵",
+ "抢",
+ "博",
+ "巧",
+ "壳",
+ "兄",
+ "杜",
+ "讯",
+ "诚",
+ "碧",
+ "祥",
+ "柯",
+ "页",
+ "巡",
+ "矩",
+ "悲",
+ "灌",
+ "龄",
+ "伦",
+ "票",
+ "寻",
+ "桂",
+ "铺",
+ "圣",
+ "恐",
+ "恰",
+ "郑",
+ "趣",
+ "抬",
+ "荒",
+ "腾",
+ "贴",
+ "柔",
+ "滴",
+ "猛",
+ "阔",
+ "辆",
+ "妻",
+ "填",
+ "撤",
+ "储",
+ "签",
+ "闹",
+ "扰",
+ "紫",
+ "砂",
+ "递",
+ "戏",
+ "吊",
+ "陶",
+ "伐",
+ "喂",
+ "疗",
+ "瓶",
+ "婆",
+ "抚",
+ "臂",
+ "摸",
+ "忍",
+ "虾",
+ "蜡",
+ "邻",
+ "胸",
+ "巩",
+ "挤",
+ "偶",
+ "弃",
+ "槽",
+ "劲",
+ "乳",
+ "邓",
+ "吉",
+ "仁",
+ "烂",
+ "砖",
+ "租",
+ "乌",
+ "舰",
+ "伴",
+ "瓜",
+ "浅",
+ "丙",
+ "暂",
+ "燥",
+ "橡",
+ "柳",
+ "迷",
+ "暖",
+ "牌",
+ "秧",
+ "胆",
+ "详",
+ "簧",
+ "踏",
+ "瓷",
+ "谱",
+ "呆",
+ "宾",
+ "糊",
+ "洛",
+ "辉",
+ "愤",
+ "竞",
+ "隙",
+ "怒",
+ "粘",
+ "乃",
+ "绪",
+ "肩",
+ "籍",
+ "敏",
+ "涂",
+ "熙",
+ "皆",
+ "侦",
+ "悬",
+ "掘",
+ "享",
+ "纠",
+ "醒",
+ "狂",
+ "锁",
+ "淀",
+ "恨",
+ "牲",
+ "霸",
+ "爬",
+ "赏",
+ "逆",
+ "玩",
+ "陵",
+ "祝",
+ "秒",
+ "浙",
+ "貌"
+ }), 1)
+ {
+ populate_maps();
+ }
+ };
+}
+
+#endif
diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h new file mode 100644 index 000000000..0d7a637e6 --- /dev/null +++ b/src/mnemonics/dutch.h @@ -0,0 +1,1686 @@ +// Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/*!
+ * \file dutch.h
+ *
+ * \brief New Dutch word list and map.
+ */
+
+#ifndef DUTCH_H
+#define DUTCH_H
+
+#include <vector>
+#include <unordered_map>
+#include "language_base.h"
+#include <string>
+
+/*!
+ * \namespace Language
+ * \brief Mnemonic language related namespace.
+ */
+namespace Language
+{
+ class Dutch: public Base
+ {
+ public:
+ Dutch(): Base("Dutch", std::vector<std::string>({
+ "aalglad",
+ "aalscholver",
+ "aambeeld",
+ "aangeef",
+ "aanlandig",
+ "aanvaard",
+ "aanwakker",
+ "aapmens",
+ "aarten",
+ "abdicatie",
+ "abnormaal",
+ "abrikoos",
+ "accu",
+ "acuut",
+ "adjudant",
+ "admiraal",
+ "advies",
+ "afbidding",
+ "afdracht",
+ "affaire",
+ "affiche",
+ "afgang",
+ "afkick",
+ "afknap",
+ "aflees",
+ "afmijner",
+ "afname",
+ "afpreekt",
+ "afrader",
+ "afspeel",
+ "aftocht",
+ "aftrek",
+ "afzijdig",
+ "ahornboom",
+ "aktetas",
+ "akzo",
+ "alchemist",
+ "alcohol",
+ "aldaar",
+ "alexander",
+ "alfabet",
+ "alfredo",
+ "alice",
+ "alikruik",
+ "allrisk",
+ "altsax",
+ "alufolie",
+ "alziend",
+ "amai",
+ "ambacht",
+ "ambieer",
+ "amina",
+ "amnestie",
+ "amok",
+ "ampul",
+ "amuzikaal",
+ "angela",
+ "aniek",
+ "antje",
+ "antwerpen",
+ "anya",
+ "aorta",
+ "apache",
+ "apekool",
+ "appelaar",
+ "arganolie",
+ "argeloos",
+ "armoede",
+ "arrenslee",
+ "artritis",
+ "arubaan",
+ "asbak",
+ "ascii",
+ "asgrauw",
+ "asjes",
+ "asml",
+ "aspunt",
+ "asurn",
+ "asveld",
+ "aterling",
+ "atomair",
+ "atrium",
+ "atsma",
+ "atypisch",
+ "auping",
+ "aura",
+ "avifauna",
+ "axiaal",
+ "azoriaan",
+ "azteek",
+ "azuur",
+ "bachelor",
+ "badderen",
+ "badhotel",
+ "badmantel",
+ "badsteden",
+ "balie",
+ "ballans",
+ "balvers",
+ "bamibal",
+ "banneling",
+ "barracuda",
+ "basaal",
+ "batelaan",
+ "batje",
+ "beambte",
+ "bedlamp",
+ "bedwelmd",
+ "befaamd",
+ "begierd",
+ "begraaf",
+ "behield",
+ "beijaard",
+ "bejaagd",
+ "bekaaid",
+ "beks",
+ "bektas",
+ "belaad",
+ "belboei",
+ "belderbos",
+ "beloerd",
+ "beluchten",
+ "bemiddeld",
+ "benadeeld",
+ "benijd",
+ "berechten",
+ "beroemd",
+ "besef",
+ "besseling",
+ "best",
+ "betichten",
+ "bevind",
+ "bevochten",
+ "bevraagd",
+ "bewust",
+ "bidplaats",
+ "biefstuk",
+ "biemans",
+ "biezen",
+ "bijbaan",
+ "bijeenkom",
+ "bijfiguur",
+ "bijkaart",
+ "bijlage",
+ "bijpaard",
+ "bijtgaar",
+ "bijweg",
+ "bimmel",
+ "binck",
+ "bint",
+ "biobak",
+ "biotisch",
+ "biseks",
+ "bistro",
+ "bitter",
+ "bitumen",
+ "bizar",
+ "blad",
+ "bleken",
+ "blender",
+ "bleu",
+ "blief",
+ "blijven",
+ "blozen",
+ "bock",
+ "boef",
+ "boei",
+ "boks",
+ "bolder",
+ "bolus",
+ "bolvormig",
+ "bomaanval",
+ "bombarde",
+ "bomma",
+ "bomtapijt",
+ "bookmaker",
+ "boos",
+ "borg",
+ "bosbes",
+ "boshuizen",
+ "bosloop",
+ "botanicus",
+ "bougie",
+ "bovag",
+ "boxspring",
+ "braad",
+ "brasem",
+ "brevet",
+ "brigade",
+ "brinckman",
+ "bruid",
+ "budget",
+ "buffel",
+ "buks",
+ "bulgaar",
+ "buma",
+ "butaan",
+ "butler",
+ "buuf",
+ "cactus",
+ "cafeetje",
+ "camcorder",
+ "cannabis",
+ "canyon",
+ "capoeira",
+ "capsule",
+ "carkit",
+ "casanova",
+ "catalaan",
+ "ceintuur",
+ "celdeling",
+ "celplasma",
+ "cement",
+ "censeren",
+ "ceramisch",
+ "cerberus",
+ "cerebraal",
+ "cesium",
+ "cirkel",
+ "citeer",
+ "civiel",
+ "claxon",
+ "clenbuterol",
+ "clicheren",
+ "clijsen",
+ "coalitie",
+ "coassistentschap",
+ "coaxiaal",
+ "codetaal",
+ "cofinanciering",
+ "cognac",
+ "coltrui",
+ "comfort",
+ "commandant",
+ "condensaat",
+ "confectie",
+ "conifeer",
+ "convector",
+ "copier",
+ "corfu",
+ "correct",
+ "coup",
+ "couvert",
+ "creatie",
+ "credit",
+ "crematie",
+ "cricket",
+ "croupier",
+ "cruciaal",
+ "cruijff",
+ "cuisine",
+ "culemborg",
+ "culinair",
+ "curve",
+ "cyrano",
+ "dactylus",
+ "dading",
+ "dagblind",
+ "dagje",
+ "daglicht",
+ "dagprijs",
+ "dagranden",
+ "dakdekker",
+ "dakpark",
+ "dakterras",
+ "dalgrond",
+ "dambord",
+ "damkat",
+ "damlengte",
+ "damman",
+ "danenberg",
+ "debbie",
+ "decibel",
+ "defect",
+ "deformeer",
+ "degelijk",
+ "degradant",
+ "dejonghe",
+ "dekken",
+ "deppen",
+ "derek",
+ "derf",
+ "derhalve",
+ "detineren",
+ "devalueer",
+ "diaken",
+ "dicht",
+ "dictaat",
+ "dief",
+ "digitaal",
+ "dijbreuk",
+ "dijkmans",
+ "dimbaar",
+ "dinsdag",
+ "diode",
+ "dirigeer",
+ "disbalans",
+ "dobermann",
+ "doenbaar",
+ "doerak",
+ "dogma",
+ "dokhaven",
+ "dokwerker",
+ "doling",
+ "dolphijn",
+ "dolven",
+ "dombo",
+ "dooraderd",
+ "dopeling",
+ "doping",
+ "draderig",
+ "drama",
+ "drenkbak",
+ "dreumes",
+ "drol",
+ "drug",
+ "duaal",
+ "dublin",
+ "duplicaat",
+ "durven",
+ "dusdanig",
+ "dutchbat",
+ "dutje",
+ "dutten",
+ "duur",
+ "duwwerk",
+ "dwaal",
+ "dweil",
+ "dwing",
+ "dyslexie",
+ "ecostroom",
+ "ecotaks",
+ "educatie",
+ "eeckhout",
+ "eede",
+ "eemland",
+ "eencellig",
+ "eeneiig",
+ "eenruiter",
+ "eenwinter",
+ "eerenberg",
+ "eerrover",
+ "eersel",
+ "eetmaal",
+ "efteling",
+ "egaal",
+ "egtberts",
+ "eickhoff",
+ "eidooier",
+ "eiland",
+ "eind",
+ "eisden",
+ "ekster",
+ "elburg",
+ "elevatie",
+ "elfkoppig",
+ "elfrink",
+ "elftal",
+ "elimineer",
+ "elleboog",
+ "elma",
+ "elodie",
+ "elsa",
+ "embleem",
+ "embolie",
+ "emoe",
+ "emonds",
+ "emplooi",
+ "enduro",
+ "enfin",
+ "engageer",
+ "entourage",
+ "entstof",
+ "epileer",
+ "episch",
+ "eppo",
+ "erasmus",
+ "erboven",
+ "erebaan",
+ "erelijst",
+ "ereronden",
+ "ereteken",
+ "erfhuis",
+ "erfwet",
+ "erger",
+ "erica",
+ "ermitage",
+ "erna",
+ "ernie",
+ "erts",
+ "ertussen",
+ "eruitzien",
+ "ervaar",
+ "erven",
+ "erwt",
+ "esbeek",
+ "escort",
+ "esdoorn",
+ "essing",
+ "etage",
+ "eter",
+ "ethanol",
+ "ethicus",
+ "etholoog",
+ "eufonisch",
+ "eurocent",
+ "evacuatie",
+ "exact",
+ "examen",
+ "executant",
+ "exen",
+ "exit",
+ "exogeen",
+ "exotherm",
+ "expeditie",
+ "expletief",
+ "expres",
+ "extase",
+ "extinctie",
+ "faal",
+ "faam",
+ "fabel",
+ "facultair",
+ "fakir",
+ "fakkel",
+ "faliekant",
+ "fallisch",
+ "famke",
+ "fanclub",
+ "fase",
+ "fatsoen",
+ "fauna",
+ "federaal",
+ "feedback",
+ "feest",
+ "feilbaar",
+ "feitelijk",
+ "felblauw",
+ "figurante",
+ "fiod",
+ "fitheid",
+ "fixeer",
+ "flap",
+ "fleece",
+ "fleur",
+ "flexibel",
+ "flits",
+ "flos",
+ "flow",
+ "fluweel",
+ "foezelen",
+ "fokkelman",
+ "fokpaard",
+ "fokvee",
+ "folder",
+ "follikel",
+ "folmer",
+ "folteraar",
+ "fooi",
+ "foolen",
+ "forfait",
+ "forint",
+ "formule",
+ "fornuis",
+ "fosfaat",
+ "foxtrot",
+ "foyer",
+ "fragiel",
+ "frater",
+ "freak",
+ "freddie",
+ "fregat",
+ "freon",
+ "frijnen",
+ "fructose",
+ "frunniken",
+ "fuiven",
+ "funshop",
+ "furieus",
+ "fysica",
+ "gadget",
+ "galder",
+ "galei",
+ "galg",
+ "galvlieg",
+ "galzuur",
+ "ganesh",
+ "gaswet",
+ "gaza",
+ "gazelle",
+ "geaaid",
+ "gebiecht",
+ "gebufferd",
+ "gedijd",
+ "geef",
+ "geflanst",
+ "gefreesd",
+ "gegaan",
+ "gegijzeld",
+ "gegniffel",
+ "gegraaid",
+ "gehikt",
+ "gehobbeld",
+ "gehucht",
+ "geiser",
+ "geiten",
+ "gekaakt",
+ "gekheid",
+ "gekijf",
+ "gekmakend",
+ "gekocht",
+ "gekskap",
+ "gekte",
+ "gelubberd",
+ "gemiddeld",
+ "geordend",
+ "gepoederd",
+ "gepuft",
+ "gerda",
+ "gerijpt",
+ "geseald",
+ "geshockt",
+ "gesierd",
+ "geslaagd",
+ "gesnaaid",
+ "getracht",
+ "getwijfel",
+ "geuit",
+ "gevecht",
+ "gevlagd",
+ "gewicht",
+ "gezaagd",
+ "gezocht",
+ "ghanees",
+ "giebelen",
+ "giechel",
+ "giepmans",
+ "gips",
+ "giraal",
+ "gistachtig",
+ "gitaar",
+ "glaasje",
+ "gletsjer",
+ "gleuf",
+ "glibberen",
+ "glijbaan",
+ "gloren",
+ "gluipen",
+ "gluren",
+ "gluur",
+ "gnoe",
+ "goddelijk",
+ "godgans",
+ "godschalk",
+ "godzalig",
+ "goeierd",
+ "gogme",
+ "goklustig",
+ "gokwereld",
+ "gonggrijp",
+ "gonje",
+ "goor",
+ "grabbel",
+ "graf",
+ "graveer",
+ "grif",
+ "grolleman",
+ "grom",
+ "groosman",
+ "grubben",
+ "gruijs",
+ "grut",
+ "guacamole",
+ "guido",
+ "guppy",
+ "haazen",
+ "hachelijk",
+ "haex",
+ "haiku",
+ "hakhout",
+ "hakken",
+ "hanegem",
+ "hans",
+ "hanteer",
+ "harrie",
+ "hazebroek",
+ "hedonist",
+ "heil",
+ "heineken",
+ "hekhuis",
+ "hekman",
+ "helbig",
+ "helga",
+ "helwegen",
+ "hengelaar",
+ "herkansen",
+ "hermafrodiet",
+ "hertaald",
+ "hiaat",
+ "hikspoors",
+ "hitachi",
+ "hitparade",
+ "hobo",
+ "hoeve",
+ "holocaust",
+ "hond",
+ "honnepon",
+ "hoogacht",
+ "hotelbed",
+ "hufter",
+ "hugo",
+ "huilbier",
+ "hulk",
+ "humus",
+ "huwbaar",
+ "huwelijk",
+ "hype",
+ "iconisch",
+ "idema",
+ "ideogram",
+ "idolaat",
+ "ietje",
+ "ijker",
+ "ijkheid",
+ "ijklijn",
+ "ijkmaat",
+ "ijkwezen",
+ "ijmuiden",
+ "ijsbox",
+ "ijsdag",
+ "ijselijk",
+ "ijskoud",
+ "ilse",
+ "immuun",
+ "impliceer",
+ "impuls",
+ "inbijten",
+ "inbuigen",
+ "indijken",
+ "induceer",
+ "indy",
+ "infecteer",
+ "inhaak",
+ "inkijk",
+ "inluiden",
+ "inmijnen",
+ "inoefenen",
+ "inpolder",
+ "inrijden",
+ "inslaan",
+ "invitatie",
+ "inwaaien",
+ "ionisch",
+ "isaac",
+ "isolatie",
+ "isotherm",
+ "isra",
+ "italiaan",
+ "ivoor",
+ "jacobs",
+ "jakob",
+ "jammen",
+ "jampot",
+ "jarig",
+ "jehova",
+ "jenever",
+ "jezus",
+ "joana",
+ "jobdienst",
+ "josua",
+ "joule",
+ "juich",
+ "jurk",
+ "juut",
+ "kaas",
+ "kabelaar",
+ "kabinet",
+ "kagenaar",
+ "kajuit",
+ "kalebas",
+ "kalm",
+ "kanjer",
+ "kapucijn",
+ "karregat",
+ "kart",
+ "katvanger",
+ "katwijk",
+ "kegelaar",
+ "keiachtig",
+ "keizer",
+ "kenletter",
+ "kerdijk",
+ "keus",
+ "kevlar",
+ "kezen",
+ "kickback",
+ "kieviet",
+ "kijken",
+ "kikvors",
+ "kilheid",
+ "kilobit",
+ "kilsdonk",
+ "kipschnitzel",
+ "kissebis",
+ "klad",
+ "klagelijk",
+ "klak",
+ "klapbaar",
+ "klaver",
+ "klene",
+ "klets",
+ "klijnhout",
+ "klit",
+ "klok",
+ "klonen",
+ "klotefilm",
+ "kluif",
+ "klumper",
+ "klus",
+ "knabbel",
+ "knagen",
+ "knaven",
+ "kneedbaar",
+ "knmi",
+ "knul",
+ "knus",
+ "kokhals",
+ "komiek",
+ "komkommer",
+ "kompaan",
+ "komrij",
+ "komvormig",
+ "koning",
+ "kopbal",
+ "kopklep",
+ "kopnagel",
+ "koppejan",
+ "koptekst",
+ "kopwand",
+ "koraal",
+ "kosmisch",
+ "kostbaar",
+ "kram",
+ "kraneveld",
+ "kras",
+ "kreling",
+ "krengen",
+ "kribbe",
+ "krik",
+ "kruid",
+ "krulbol",
+ "kuijper",
+ "kuipbank",
+ "kuit",
+ "kuiven",
+ "kutsmoes",
+ "kuub",
+ "kwak",
+ "kwatong",
+ "kwetsbaar",
+ "kwezelaar",
+ "kwijnen",
+ "kwik",
+ "kwinkslag",
+ "kwitantie",
+ "lading",
+ "lakbeits",
+ "lakken",
+ "laklaag",
+ "lakmoes",
+ "lakwijk",
+ "lamheid",
+ "lamp",
+ "lamsbout",
+ "lapmiddel",
+ "larve",
+ "laser",
+ "latijn",
+ "latuw",
+ "lawaai",
+ "laxeerpil",
+ "lebberen",
+ "ledeboer",
+ "leefbaar",
+ "leeman",
+ "lefdoekje",
+ "lefhebber",
+ "legboor",
+ "legsel",
+ "leguaan",
+ "leiplaat",
+ "lekdicht",
+ "lekrijden",
+ "leksteen",
+ "lenen",
+ "leraar",
+ "lesbienne",
+ "leugenaar",
+ "leut",
+ "lexicaal",
+ "lezing",
+ "lieten",
+ "liggeld",
+ "lijdzaam",
+ "lijk",
+ "lijmstang",
+ "lijnschip",
+ "likdoorn",
+ "likken",
+ "liksteen",
+ "limburg",
+ "link",
+ "linoleum",
+ "lipbloem",
+ "lipman",
+ "lispelen",
+ "lissabon",
+ "litanie",
+ "liturgie",
+ "lochem",
+ "loempia",
+ "loesje",
+ "logheid",
+ "lonen",
+ "lonneke",
+ "loom",
+ "loos",
+ "losbaar",
+ "loslaten",
+ "losplaats",
+ "loting",
+ "lotnummer",
+ "lots",
+ "louie",
+ "lourdes",
+ "louter",
+ "lowbudget",
+ "luijten",
+ "luikenaar",
+ "luilak",
+ "luipaard",
+ "luizenbos",
+ "lulkoek",
+ "lumen",
+ "lunzen",
+ "lurven",
+ "lutjeboer",
+ "luttel",
+ "lutz",
+ "luuk",
+ "luwte",
+ "luyendijk",
+ "lyceum",
+ "lynx",
+ "maakbaar",
+ "magdalena",
+ "malheid",
+ "manchet",
+ "manfred",
+ "manhaftig",
+ "mank",
+ "mantel",
+ "marion",
+ "marxist",
+ "masmeijer",
+ "massaal",
+ "matsen",
+ "matverf",
+ "matze",
+ "maude",
+ "mayonaise",
+ "mechanica",
+ "meifeest",
+ "melodie",
+ "meppelink",
+ "midvoor",
+ "midweeks",
+ "midzomer",
+ "miezel",
+ "mijnraad",
+ "minus",
+ "mirck",
+ "mirte",
+ "mispakken",
+ "misraden",
+ "miswassen",
+ "mitella",
+ "moker",
+ "molecule",
+ "mombakkes",
+ "moonen",
+ "mopperaar",
+ "moraal",
+ "morgana",
+ "mormel",
+ "mosselaar",
+ "motregen",
+ "mouw",
+ "mufheid",
+ "mutueel",
+ "muzelman",
+ "naaidoos",
+ "naald",
+ "nadeel",
+ "nadruk",
+ "nagy",
+ "nahon",
+ "naima",
+ "nairobi",
+ "napalm",
+ "napels",
+ "napijn",
+ "napoleon",
+ "narigheid",
+ "narratief",
+ "naseizoen",
+ "nasibal",
+ "navigatie",
+ "nawijn",
+ "negatief",
+ "nekletsel",
+ "nekwervel",
+ "neolatijn",
+ "neonataal",
+ "neptunus",
+ "nerd",
+ "nest",
+ "neuzelaar",
+ "nihiliste",
+ "nijenhuis",
+ "nijging",
+ "nijhoff",
+ "nijl",
+ "nijptang",
+ "nippel",
+ "nokkenas",
+ "noordam",
+ "noren",
+ "normaal",
+ "nottelman",
+ "notulant",
+ "nout",
+ "nuance",
+ "nuchter",
+ "nudorp",
+ "nulde",
+ "nullijn",
+ "nulmeting",
+ "nunspeet",
+ "nylon",
+ "obelisk",
+ "object",
+ "oblie",
+ "obsceen",
+ "occlusie",
+ "oceaan",
+ "ochtend",
+ "ockhuizen",
+ "oerdom",
+ "oergezond",
+ "oerlaag",
+ "oester",
+ "okhuijsen",
+ "olifant",
+ "olijfboer",
+ "omaans",
+ "ombudsman",
+ "omdat",
+ "omdijken",
+ "omdoen",
+ "omgebouwd",
+ "omkeer",
+ "omkomen",
+ "ommegaand",
+ "ommuren",
+ "omroep",
+ "omruil",
+ "omslaan",
+ "omsmeden",
+ "omvaar",
+ "onaardig",
+ "onedel",
+ "onenig",
+ "onheilig",
+ "onrecht",
+ "onroerend",
+ "ontcijfer",
+ "onthaal",
+ "ontvallen",
+ "ontzadeld",
+ "onzacht",
+ "onzin",
+ "onzuiver",
+ "oogappel",
+ "ooibos",
+ "ooievaar",
+ "ooit",
+ "oorarts",
+ "oorhanger",
+ "oorijzer",
+ "oorklep",
+ "oorschelp",
+ "oorworm",
+ "oorzaak",
+ "opdagen",
+ "opdien",
+ "opdweilen",
+ "opel",
+ "opgebaard",
+ "opinie",
+ "opjutten",
+ "opkijken",
+ "opklaar",
+ "opkuisen",
+ "opkwam",
+ "opnaaien",
+ "opossum",
+ "opsieren",
+ "opsmeer",
+ "optreden",
+ "opvijzel",
+ "opvlammen",
+ "opwind",
+ "oraal",
+ "orchidee",
+ "orkest",
+ "ossuarium",
+ "ostendorf",
+ "oublie",
+ "oudachtig",
+ "oudbakken",
+ "oudnoors",
+ "oudshoorn",
+ "oudtante",
+ "oven",
+ "over",
+ "oxidant",
+ "pablo",
+ "pacht",
+ "paktafel",
+ "pakzadel",
+ "paljas",
+ "panharing",
+ "papfles",
+ "paprika",
+ "parochie",
+ "paus",
+ "pauze",
+ "paviljoen",
+ "peek",
+ "pegel",
+ "peigeren",
+ "pekela",
+ "pendant",
+ "penibel",
+ "pepmiddel",
+ "peptalk",
+ "periferie",
+ "perron",
+ "pessarium",
+ "peter",
+ "petfles",
+ "petgat",
+ "peuk",
+ "pfeifer",
+ "picknick",
+ "pief",
+ "pieneman",
+ "pijlkruid",
+ "pijnacker",
+ "pijpelink",
+ "pikdonker",
+ "pikeer",
+ "pilaar",
+ "pionier",
+ "pipet",
+ "piscine",
+ "pissebed",
+ "pitchen",
+ "pixel",
+ "plamuren",
+ "plan",
+ "plausibel",
+ "plegen",
+ "plempen",
+ "pleonasme",
+ "plezant",
+ "podoloog",
+ "pofmouw",
+ "pokdalig",
+ "ponywagen",
+ "popachtig",
+ "popidool",
+ "porren",
+ "positie",
+ "potten",
+ "pralen",
+ "prezen",
+ "prijzen",
+ "privaat",
+ "proef",
+ "prooi",
+ "prozawerk",
+ "pruik",
+ "prul",
+ "publiceer",
+ "puck",
+ "puilen",
+ "pukkelig",
+ "pulveren",
+ "pupil",
+ "puppy",
+ "purmerend",
+ "pustjens",
+ "putemmer",
+ "puzzelaar",
+ "queenie",
+ "quiche",
+ "raam",
+ "raar",
+ "raat",
+ "raes",
+ "ralf",
+ "rally",
+ "ramona",
+ "ramselaar",
+ "ranonkel",
+ "rapen",
+ "rapunzel",
+ "rarekiek",
+ "rarigheid",
+ "rattenhol",
+ "ravage",
+ "reactie",
+ "recreant",
+ "redacteur",
+ "redster",
+ "reewild",
+ "regie",
+ "reijnders",
+ "rein",
+ "replica",
+ "revanche",
+ "rigide",
+ "rijbaan",
+ "rijdansen",
+ "rijgen",
+ "rijkdom",
+ "rijles",
+ "rijnwijn",
+ "rijpma",
+ "rijstafel",
+ "rijtaak",
+ "rijzwepen",
+ "rioleer",
+ "ripdeal",
+ "riphagen",
+ "riskant",
+ "rits",
+ "rivaal",
+ "robbedoes",
+ "robot",
+ "rockact",
+ "rodijk",
+ "rogier",
+ "rohypnol",
+ "rollaag",
+ "rolpaal",
+ "roltafel",
+ "roof",
+ "roon",
+ "roppen",
+ "rosbief",
+ "rosharig",
+ "rosielle",
+ "rotan",
+ "rotleven",
+ "rotten",
+ "rotvaart",
+ "royaal",
+ "royeer",
+ "rubato",
+ "ruby",
+ "ruche",
+ "rudge",
+ "ruggetje",
+ "rugnummer",
+ "rugpijn",
+ "rugtitel",
+ "rugzak",
+ "ruilbaar",
+ "ruis",
+ "ruit",
+ "rukwind",
+ "rulijs",
+ "rumoeren",
+ "rumsdorp",
+ "rumtaart",
+ "runnen",
+ "russchen",
+ "ruwkruid",
+ "saboteer",
+ "saksisch",
+ "salade",
+ "salpeter",
+ "sambabal",
+ "samsam",
+ "satelliet",
+ "satineer",
+ "saus",
+ "scampi",
+ "scarabee",
+ "scenario",
+ "schobben",
+ "schubben",
+ "scout",
+ "secessie",
+ "secondair",
+ "seculair",
+ "sediment",
+ "seeland",
+ "settelen",
+ "setwinst",
+ "sheriff",
+ "shiatsu",
+ "siciliaan",
+ "sidderaal",
+ "sigma",
+ "sijben",
+ "silvana",
+ "simkaart",
+ "sinds",
+ "situatie",
+ "sjaak",
+ "sjardijn",
+ "sjezen",
+ "sjor",
+ "skinhead",
+ "skylab",
+ "slamixen",
+ "sleijpen",
+ "slijkerig",
+ "slordig",
+ "slowaak",
+ "sluieren",
+ "smadelijk",
+ "smiecht",
+ "smoel",
+ "smos",
+ "smukken",
+ "snackcar",
+ "snavel",
+ "sneaker",
+ "sneu",
+ "snijdbaar",
+ "snit",
+ "snorder",
+ "soapbox",
+ "soetekouw",
+ "soigneren",
+ "sojaboon",
+ "solo",
+ "solvabel",
+ "somber",
+ "sommatie",
+ "soort",
+ "soppen",
+ "sopraan",
+ "soundbar",
+ "spanen",
+ "spawater",
+ "spijgat",
+ "spinaal",
+ "spionage",
+ "spiraal",
+ "spleet",
+ "splijt",
+ "spoed",
+ "sporen",
+ "spul",
+ "spuug",
+ "spuw",
+ "stalen",
+ "standaard",
+ "star",
+ "stefan",
+ "stencil",
+ "stijf",
+ "stil",
+ "stip",
+ "stopdas",
+ "stoten",
+ "stoven",
+ "straat",
+ "strobbe",
+ "strubbel",
+ "stucadoor",
+ "stuif",
+ "stukadoor",
+ "subhoofd",
+ "subregent",
+ "sudoku",
+ "sukade",
+ "sulfaat",
+ "surinaams",
+ "suus",
+ "syfilis",
+ "symboliek",
+ "sympathie",
+ "synagoge",
+ "synchroon",
+ "synergie",
+ "systeem",
+ "taanderij",
+ "tabak",
+ "tachtig",
+ "tackelen",
+ "taiwanees",
+ "talman",
+ "tamheid",
+ "tangaslip",
+ "taps",
+ "tarkan",
+ "tarwe",
+ "tasman",
+ "tatjana",
+ "taxameter",
+ "teil",
+ "teisman",
+ "telbaar",
+ "telco",
+ "telganger",
+ "telstar",
+ "tenant",
+ "tepel",
+ "terzet",
+ "testament",
+ "ticket",
+ "tiesinga",
+ "tijdelijk",
+ "tika",
+ "tiksel",
+ "tilleman",
+ "timbaal",
+ "tinsteen",
+ "tiplijn",
+ "tippelaar",
+ "tjirpen",
+ "toezeggen",
+ "tolbaas",
+ "tolgeld",
+ "tolhek",
+ "tolo",
+ "tolpoort",
+ "toltarief",
+ "tolvrij",
+ "tomaat",
+ "tondeuse",
+ "toog",
+ "tooi",
+ "toonbaar",
+ "toos",
+ "topclub",
+ "toppen",
+ "toptalent",
+ "topvrouw",
+ "toque",
+ "torment",
+ "tornado",
+ "tosti",
+ "totdat",
+ "toucheer",
+ "toulouse",
+ "tournedos",
+ "tout",
+ "trabant",
+ "tragedie",
+ "trailer",
+ "traject",
+ "traktaat",
+ "trauma",
+ "tray",
+ "trechter",
+ "tred",
+ "tref",
+ "treur",
+ "troebel",
+ "tros",
+ "trucage",
+ "truffel",
+ "tsaar",
+ "tucht",
+ "tuenter",
+ "tuitelig",
+ "tukje",
+ "tuktuk",
+ "tulp",
+ "tuma",
+ "tureluurs",
+ "twijfel",
+ "twitteren",
+ "tyfoon",
+ "typograaf",
+ "ugandees",
+ "uiachtig",
+ "uier",
+ "uisnipper",
+ "ultiem",
+ "unitair",
+ "uranium",
+ "urbaan",
+ "urendag",
+ "ursula",
+ "uurcirkel",
+ "uurglas",
+ "uzelf",
+ "vaat",
+ "vakantie",
+ "vakleraar",
+ "valbijl",
+ "valpartij",
+ "valreep",
+ "valuatie",
+ "vanmiddag",
+ "vanonder",
+ "varaan",
+ "varken",
+ "vaten",
+ "veenbes",
+ "veeteler",
+ "velgrem",
+ "vellekoop",
+ "velvet",
+ "veneberg",
+ "venlo",
+ "vent",
+ "venusberg",
+ "venw",
+ "veredeld",
+ "verf",
+ "verhaaf",
+ "vermaak",
+ "vernaaid",
+ "verraad",
+ "vers",
+ "veruit",
+ "verzaagd",
+ "vetachtig",
+ "vetlok",
+ "vetmesten",
+ "veto",
+ "vetrek",
+ "vetstaart",
+ "vetten",
+ "veurink",
+ "viaduct",
+ "vibrafoon",
+ "vicariaat",
+ "vieux",
+ "vieveen",
+ "vijfvoud",
+ "villa",
+ "vilt",
+ "vimmetje",
+ "vindbaar",
+ "vips",
+ "virtueel",
+ "visdieven",
+ "visee",
+ "visie",
+ "vlaag",
+ "vleugel",
+ "vmbo",
+ "vocht",
+ "voesenek",
+ "voicemail",
+ "voip",
+ "volg",
+ "vork",
+ "vorselaar",
+ "voyeur",
+ "vracht",
+ "vrekkig",
+ "vreten",
+ "vrije",
+ "vrozen",
+ "vrucht",
+ "vucht",
+ "vugt",
+ "vulkaan",
+ "vulmiddel",
+ "vulva",
+ "vuren",
+ "waas",
+ "wacht",
+ "wadvogel",
+ "wafel",
+ "waffel",
+ "walhalla",
+ "walnoot",
+ "walraven",
+ "wals",
+ "walvis",
+ "wandaad",
+ "wanen",
+ "wanmolen",
+ "want",
+ "warklomp",
+ "warm",
+ "wasachtig",
+ "wasteil",
+ "watt",
+ "webhandel",
+ "weblog",
+ "webpagina",
+ "webzine",
+ "wedereis",
+ "wedstrijd",
+ "weeda",
+ "weert",
+ "wegmaaien",
+ "wegscheer",
+ "wekelijks",
+ "wekken",
+ "wekroep",
+ "wektoon",
+ "weldaad",
+ "welwater",
+ "wendbaar",
+ "wenkbrauw",
+ "wens",
+ "wentelaar",
+ "wervel",
+ "wesseling",
+ "wetboek",
+ "wetmatig",
+ "whirlpool",
+ "wijbrands",
+ "wijdbeens",
+ "wijk",
+ "wijnbes",
+ "wijting",
+ "wild",
+ "wimpelen",
+ "wingebied",
+ "winplaats",
+ "winter",
+ "winzucht",
+ "wipstaart",
+ "wisgerhof",
+ "withaar",
+ "witmaker",
+ "wokkel",
+ "wolf",
+ "wonenden",
+ "woning",
+ "worden",
+ "worp",
+ "wortel",
+ "wrat",
+ "wrijf",
+ "wringen",
+ "yoghurt",
+ "ypsilon",
+ "zaaijer",
+ "zaak",
+ "zacharias",
+ "zakelijk",
+ "zakkam",
+ "zakwater",
+ "zalf",
+ "zalig",
+ "zaniken",
+ "zebracode",
+ "zeeblauw",
+ "zeef",
+ "zeegaand",
+ "zeeuw",
+ "zege",
+ "zegje",
+ "zeil",
+ "zesbaans",
+ "zesenhalf",
+ "zeskantig",
+ "zesmaal",
+ "zetbaas",
+ "zetpil",
+ "zeulen",
+ "ziezo",
+ "zigzag",
+ "zijaltaar",
+ "zijbeuk",
+ "zijlijn",
+ "zijmuur",
+ "zijn",
+ "zijwaarts",
+ "zijzelf",
+ "zilt",
+ "zimmerman",
+ "zinledig",
+ "zinnelijk",
+ "zionist",
+ "zitdag",
+ "zitruimte",
+ "zitzak",
+ "zoal",
+ "zodoende",
+ "zoekbots",
+ "zoem",
+ "zoiets",
+ "zojuist",
+ "zondaar",
+ "zotskap",
+ "zottebol",
+ "zucht",
+ "zuivel",
+ "zulk",
+ "zult",
+ "zuster",
+ "zuur",
+ "zweedijk",
+ "zwendel",
+ "zwepen",
+ "zwiep",
+ "zwijmel",
+ "zworen"
+ }), 4)
+ {
+ populate_maps();
+ }
+ };
+}
+
+#endif
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index f0e254ba4..501495f0b 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -51,7 +51,10 @@ #include <boost/crc.hpp> #include <boost/algorithm/string/join.hpp> +#include "chinese_simplified.h" #include "english.h" +#include "dutch.h" +#include "french.h" #include "italian.h" #include "german.h" #include "spanish.h" @@ -82,7 +85,10 @@ namespace { // If there's a new language added, add an instance of it here. std::vector<Language::Base*> language_instances({ + Language::Singleton<Language::Chinese_Simplified>::instance(), Language::Singleton<Language::English>::instance(), + Language::Singleton<Language::Dutch>::instance(), + Language::Singleton<Language::French>::instance(), Language::Singleton<Language::Spanish>::instance(), Language::Singleton<Language::German>::instance(), Language::Singleton<Language::Italian>::instance(), @@ -236,7 +242,7 @@ namespace crypto std::vector<std::string> seed; boost::algorithm::trim(words); - boost::split(seed, words, boost::is_any_of(" ")); + boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on); // error on non-compliant word list if (seed.size() != seed_length/2 && seed.size() != seed_length && @@ -312,6 +318,14 @@ namespace crypto { language = Language::Singleton<Language::English>::instance(); } + else if (language_name == "Dutch") + { + language = Language::Singleton<Language::Dutch>::instance(); + } + else if (language_name == "French") + { + language = Language::Singleton<Language::French>::instance(); + } else if (language_name == "Spanish") { language = Language::Singleton<Language::Spanish>::instance(); @@ -336,6 +350,10 @@ namespace crypto { language = Language::Singleton<Language::Russian>::instance(); } + else if (language_name == "Chinese (Simplified)") + { + language = Language::Singleton<Language::Chinese_Simplified>::instance(); + } else { return false; @@ -381,7 +399,10 @@ namespace crypto void get_language_list(std::vector<std::string> &languages) { std::vector<Language::Base*> language_instances({ + Language::Singleton<Language::Chinese_Simplified>::instance(), Language::Singleton<Language::English>::instance(), + Language::Singleton<Language::Dutch>::instance(), + Language::Singleton<Language::French>::instance(), Language::Singleton<Language::Spanish>::instance(), Language::Singleton<Language::German>::instance(), Language::Singleton<Language::Italian>::instance(), diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 29a9f62f2..d1aa65939 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h index c42b752b1..e6cfa8951 100644 --- a/src/mnemonics/english.h +++ b/src/mnemonics/english.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -49,9 +49,7 @@ namespace Language class English: public Base
{
public:
- English()
- {
- word_list = new std::vector<std::string>({
+ English(): Base("English", std::vector<std::string>({
"abbey",
"abducts",
"ability",
@@ -1678,11 +1676,8 @@ namespace Language "zombie",
"zones",
"zoom"
- });
- unique_prefix_length = 3;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "English";
+ }), 3)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h new file mode 100644 index 000000000..6cf44a197 --- /dev/null +++ b/src/mnemonics/french.h @@ -0,0 +1,1686 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/*! + * \file french.h + * + * \brief French word list and map. + */ + +#ifndef FRENCH_H +#define FRENCH_H + +#include <vector> +#include <unordered_map> +#include "language_base.h" +#include <string> + +/*! + * \namespace Language + * \brief Mnemonic language related namespace. + */ +namespace Language +{ + class French: public Base + { + public: + French(): Base("French", std::vector<std::string>({ + "abandon", + "abattre", + "aboi", + "abolir", + "aborder", + "abri", + "absence", + "absolu", + "abuser", + "acacia", + "acajou", + "accent", + "accord", + "accrocher", + "accuser", + "acerbe", + "achat", + "acheter", + "acide", + "acier", + "acquis", + "acte", + "action", + "adage", + "adepte", + "adieu", + "admettre", + "admis", + "adorer", + "adresser", + "aduler", + "affaire", + "affirmer", + "afin", + "agacer", + "agent", + "agir", + "agiter", + "agonie", + "agrafe", + "agrume", + "aider", + "aigle", + "aigre", + "aile", + "ailleurs", + "aimant", + "aimer", + "ainsi", + "aise", + "ajouter", + "alarme", + "album", + "alcool", + "alerte", + "algue", + "alibi", + "aller", + "allumer", + "alors", + "amande", + "amener", + "amie", + "amorcer", + "amour", + "ample", + "amuser", + "ananas", + "ancien", + "anglais", + "angoisse", + "animal", + "anneau", + "annoncer", + "apercevoir", + "apparence", + "appel", + "apporter", + "apprendre", + "appuyer", + "arbre", + "arcade", + "arceau", + "arche", + "ardeur", + "argent", + "argile", + "aride", + "arme", + "armure", + "arracher", + "arriver", + "article", + "asile", + "aspect", + "assaut", + "assez", + "assister", + "assurer", + "astre", + "astuce", + "atlas", + "atroce", + "attacher", + "attente", + "attirer", + "aube", + "aucun", + "audace", + "auparavant", + "auquel", + "aurore", + "aussi", + "autant", + "auteur", + "autoroute", + "autre", + "aval", + "avant", + "avec", + "avenir", + "averse", + "aveu", + "avide", + "avion", + "avis", + "avoir", + "avouer", + "avril", + "azote", + "azur", + "badge", + "bagage", + "bague", + "bain", + "baisser", + "balai", + "balcon", + "balise", + "balle", + "bambou", + "banane", + "banc", + "bandage", + "banjo", + "banlieue", + "bannir", + "banque", + "baobab", + "barbe", + "barque", + "barrer", + "bassine", + "bataille", + "bateau", + "battre", + "baver", + "bavoir", + "bazar", + "beau", + "beige", + "berger", + "besoin", + "beurre", + "biais", + "biceps", + "bidule", + "bien", + "bijou", + "bilan", + "billet", + "blanc", + "blason", + "bleu", + "bloc", + "blond", + "bocal", + "boire", + "boiserie", + "boiter", + "bonbon", + "bondir", + "bonheur", + "bordure", + "borgne", + "borner", + "bosse", + "bouche", + "bouder", + "bouger", + "boule", + "bourse", + "bout", + "boxe", + "brader", + "braise", + "branche", + "braquer", + "bras", + "brave", + "brebis", + "brevet", + "brider", + "briller", + "brin", + "brique", + "briser", + "broche", + "broder", + "bronze", + "brosser", + "brouter", + "bruit", + "brute", + "budget", + "buffet", + "bulle", + "bureau", + "buriner", + "buste", + "buter", + "butiner", + "cabas", + "cabinet", + "cabri", + "cacao", + "cacher", + "cadeau", + "cadre", + "cage", + "caisse", + "caler", + "calme", + "camarade", + "camion", + "campagne", + "canal", + "canif", + "capable", + "capot", + "carat", + "caresser", + "carie", + "carpe", + "cartel", + "casier", + "casque", + "casserole", + "cause", + "cavale", + "cave", + "ceci", + "cela", + "celui", + "cendre", + "cent", + "cependant", + "cercle", + "cerise", + "cerner", + "certes", + "cerveau", + "cesser", + "chacun", + "chair", + "chaleur", + "chamois", + "chanson", + "chaque", + "charge", + "chasse", + "chat", + "chaud", + "chef", + "chemin", + "cheveu", + "chez", + "chicane", + "chien", + "chiffre", + "chiner", + "chiot", + "chlore", + "choc", + "choix", + "chose", + "chou", + "chute", + "cibler", + "cidre", + "ciel", + "cigale", + "cinq", + "cintre", + "cirage", + "cirque", + "ciseau", + "citation", + "citer", + "citron", + "civet", + "clairon", + "clan", + "classe", + "clavier", + "clef", + "climat", + "cloche", + "cloner", + "clore", + "clos", + "clou", + "club", + "cobra", + "cocon", + "coiffer", + "coin", + "colline", + "colon", + "combat", + "comme", + "compte", + "conclure", + "conduire", + "confier", + "connu", + "conseil", + "contre", + "convenir", + "copier", + "cordial", + "cornet", + "corps", + "cosmos", + "coton", + "couche", + "coude", + "couler", + "coupure", + "cour", + "couteau", + "couvrir", + "crabe", + "crainte", + "crampe", + "cran", + "creuser", + "crever", + "crier", + "crime", + "crin", + "crise", + "crochet", + "croix", + "cruel", + "cuisine", + "cuite", + "culot", + "culte", + "cumul", + "cure", + "curieux", + "cuve", + "dame", + "danger", + "dans", + "davantage", + "debout", + "dedans", + "dehors", + "delta", + "demain", + "demeurer", + "demi", + "dense", + "dent", + "depuis", + "dernier", + "descendre", + "dessus", + "destin", + "dette", + "deuil", + "deux", + "devant", + "devenir", + "devin", + "devoir", + "dicton", + "dieu", + "difficile", + "digestion", + "digue", + "diluer", + "dimanche", + "dinde", + "diode", + "dire", + "diriger", + "discours", + "disposer", + "distance", + "divan", + "divers", + "docile", + "docteur", + "dodu", + "dogme", + "doigt", + "dominer", + "donation", + "donjon", + "donner", + "dopage", + "dorer", + "dormir", + "doseur", + "douane", + "double", + "douche", + "douleur", + "doute", + "doux", + "douzaine", + "draguer", + "drame", + "drap", + "dresser", + "droit", + "duel", + "dune", + "duper", + "durant", + "durcir", + "durer", + "eaux", + "effacer", + "effet", + "effort", + "effrayant", + "elle", + "embrasser", + "emmener", + "emparer", + "empire", + "employer", + "emporter", + "enclos", + "encore", + "endive", + "endormir", + "endroit", + "enduit", + "enfant", + "enfermer", + "enfin", + "enfler", + "enfoncer", + "enfuir", + "engager", + "engin", + "enjeu", + "enlever", + "ennemi", + "ennui", + "ensemble", + "ensuite", + "entamer", + "entendre", + "entier", + "entourer", + "entre", + "envelopper", + "envie", + "envoyer", + "erreur", + "escalier", + "espace", + "espoir", + "esprit", + "essai", + "essor", + "essuyer", + "estimer", + "exact", + "examiner", + "excuse", + "exemple", + "exiger", + "exil", + "exister", + "exode", + "expliquer", + "exposer", + "exprimer", + "extase", + "fable", + "facette", + "facile", + "fade", + "faible", + "faim", + "faire", + "fait", + "falloir", + "famille", + "faner", + "farce", + "farine", + "fatigue", + "faucon", + "faune", + "faute", + "faux", + "faveur", + "favori", + "faxer", + "feinter", + "femme", + "fendre", + "fente", + "ferme", + "festin", + "feuille", + "feutre", + "fiable", + "fibre", + "ficher", + "fier", + "figer", + "figure", + "filet", + "fille", + "filmer", + "fils", + "filtre", + "final", + "finesse", + "finir", + "fiole", + "firme", + "fixe", + "flacon", + "flair", + "flamme", + "flan", + "flaque", + "fleur", + "flocon", + "flore", + "flot", + "flou", + "fluide", + "fluor", + "flux", + "focus", + "foin", + "foire", + "foison", + "folie", + "fonction", + "fondre", + "fonte", + "force", + "forer", + "forger", + "forme", + "fort", + "fosse", + "fouet", + "fouine", + "foule", + "four", + "foyer", + "frais", + "franc", + "frapper", + "freiner", + "frimer", + "friser", + "frite", + "froid", + "froncer", + "fruit", + "fugue", + "fuir", + "fuite", + "fumer", + "fureur", + "furieux", + "fuser", + "fusil", + "futile", + "futur", + "gagner", + "gain", + "gala", + "galet", + "galop", + "gamme", + "gant", + "garage", + "garde", + "garer", + "gauche", + "gaufre", + "gaule", + "gaver", + "gazon", + "geler", + "genou", + "genre", + "gens", + "gercer", + "germer", + "geste", + "gibier", + "gicler", + "gilet", + "girafe", + "givre", + "glace", + "glisser", + "globe", + "gloire", + "gluant", + "gober", + "golf", + "gommer", + "gorge", + "gosier", + "goutte", + "grain", + "gramme", + "grand", + "gras", + "grave", + "gredin", + "griffure", + "griller", + "gris", + "gronder", + "gros", + "grotte", + "groupe", + "grue", + "guerrier", + "guetter", + "guider", + "guise", + "habiter", + "hache", + "haie", + "haine", + "halte", + "hamac", + "hanche", + "hangar", + "hanter", + "haras", + "hareng", + "harpe", + "hasard", + "hausse", + "haut", + "havre", + "herbe", + "heure", + "hibou", + "hier", + "histoire", + "hiver", + "hochet", + "homme", + "honneur", + "honte", + "horde", + "horizon", + "hormone", + "houle", + "housse", + "hublot", + "huile", + "huit", + "humain", + "humble", + "humide", + "humour", + "hurler", + "idole", + "igloo", + "ignorer", + "illusion", + "image", + "immense", + "immobile", + "imposer", + "impression", + "incapable", + "inconnu", + "index", + "indiquer", + "infime", + "injure", + "inox", + "inspirer", + "instant", + "intention", + "intime", + "inutile", + "inventer", + "inviter", + "iode", + "iris", + "issue", + "ivre", + "jade", + "jadis", + "jamais", + "jambe", + "janvier", + "jardin", + "jauge", + "jaunisse", + "jeter", + "jeton", + "jeudi", + "jeune", + "joie", + "joindre", + "joli", + "joueur", + "journal", + "judo", + "juge", + "juillet", + "juin", + "jument", + "jungle", + "jupe", + "jupon", + "jurer", + "juron", + "jury", + "jusque", + "juste", + "kayak", + "ketchup", + "kilo", + "kiwi", + "koala", + "label", + "lacet", + "lacune", + "laine", + "laisse", + "lait", + "lame", + "lancer", + "lande", + "laque", + "lard", + "largeur", + "larme", + "larve", + "lasso", + "laver", + "lendemain", + "lentement", + "lequel", + "lettre", + "leur", + "lever", + "levure", + "liane", + "libre", + "lien", + "lier", + "lieutenant", + "ligne", + "ligoter", + "liguer", + "limace", + "limer", + "limite", + "lingot", + "lion", + "lire", + "lisser", + "litre", + "livre", + "lobe", + "local", + "logis", + "loin", + "loisir", + "long", + "loque", + "lors", + "lotus", + "louer", + "loup", + "lourd", + "louve", + "loyer", + "lubie", + "lucide", + "lueur", + "luge", + "luire", + "lundi", + "lune", + "lustre", + "lutin", + "lutte", + "luxe", + "machine", + "madame", + "magie", + "magnifique", + "magot", + "maigre", + "main", + "mairie", + "maison", + "malade", + "malheur", + "malin", + "manche", + "manger", + "manier", + "manoir", + "manquer", + "marche", + "mardi", + "marge", + "mariage", + "marquer", + "mars", + "masque", + "masse", + "matin", + "mauvais", + "meilleur", + "melon", + "membre", + "menacer", + "mener", + "mensonge", + "mentir", + "menu", + "merci", + "merlu", + "mesure", + "mettre", + "meuble", + "meunier", + "meute", + "miche", + "micro", + "midi", + "miel", + "miette", + "mieux", + "milieu", + "mille", + "mimer", + "mince", + "mineur", + "ministre", + "minute", + "mirage", + "miroir", + "miser", + "mite", + "mixte", + "mobile", + "mode", + "module", + "moins", + "mois", + "moment", + "momie", + "monde", + "monsieur", + "monter", + "moquer", + "moral", + "morceau", + "mordre", + "morose", + "morse", + "mortier", + "morue", + "motif", + "motte", + "moudre", + "moule", + "mourir", + "mousse", + "mouton", + "mouvement", + "moyen", + "muer", + "muette", + "mugir", + "muguet", + "mulot", + "multiple", + "munir", + "muret", + "muse", + "musique", + "muter", + "nacre", + "nager", + "nain", + "naissance", + "narine", + "narrer", + "naseau", + "nasse", + "nation", + "nature", + "naval", + "navet", + "naviguer", + "navrer", + "neige", + "nerf", + "nerveux", + "neuf", + "neutre", + "neuve", + "neveu", + "niche", + "nier", + "niveau", + "noble", + "noce", + "nocif", + "noir", + "nomade", + "nombre", + "nommer", + "nord", + "norme", + "notaire", + "notice", + "notre", + "nouer", + "nougat", + "nourrir", + "nous", + "nouveau", + "novice", + "noyade", + "noyer", + "nuage", + "nuance", + "nuire", + "nuit", + "nulle", + "nuque", + "oasis", + "objet", + "obliger", + "obscur", + "observer", + "obtenir", + "obus", + "occasion", + "occuper", + "ocre", + "octet", + "odeur", + "odorat", + "offense", + "officier", + "offrir", + "ogive", + "oiseau", + "olive", + "ombre", + "onctueux", + "onduler", + "ongle", + "onze", + "opter", + "option", + "orageux", + "oral", + "orange", + "orbite", + "ordinaire", + "ordre", + "oreille", + "organe", + "orgie", + "orgueil", + "orient", + "origan", + "orner", + "orteil", + "ortie", + "oser", + "osselet", + "otage", + "otarie", + "ouate", + "oublier", + "ouest", + "ours", + "outil", + "outre", + "ouvert", + "ouvrir", + "ovale", + "ozone", + "pacte", + "page", + "paille", + "pain", + "paire", + "paix", + "palace", + "palissade", + "palmier", + "palpiter", + "panda", + "panneau", + "papa", + "papier", + "paquet", + "parc", + "pardi", + "parfois", + "parler", + "parmi", + "parole", + "partir", + "parvenir", + "passer", + "pastel", + "patin", + "patron", + "paume", + "pause", + "pauvre", + "paver", + "pavot", + "payer", + "pays", + "peau", + "peigne", + "peinture", + "pelage", + "pelote", + "pencher", + "pendre", + "penser", + "pente", + "percer", + "perdu", + "perle", + "permettre", + "personne", + "perte", + "peser", + "pesticide", + "petit", + "peuple", + "peur", + "phase", + "photo", + "phrase", + "piano", + "pied", + "pierre", + "pieu", + "pile", + "pilier", + "pilote", + "pilule", + "piment", + "pincer", + "pinson", + "pinte", + "pion", + "piquer", + "pirate", + "pire", + "piste", + "piton", + "pitre", + "pivot", + "pizza", + "placer", + "plage", + "plaire", + "plan", + "plaque", + "plat", + "plein", + "pleurer", + "pliage", + "plier", + "plonger", + "plot", + "pluie", + "plume", + "plus", + "pneu", + "poche", + "podium", + "poids", + "poil", + "point", + "poire", + "poison", + "poitrine", + "poivre", + "police", + "pollen", + "pomme", + "pompier", + "poncer", + "pondre", + "pont", + "portion", + "poser", + "position", + "possible", + "poste", + "potage", + "potin", + "pouce", + "poudre", + "poulet", + "poumon", + "poupe", + "pour", + "pousser", + "poutre", + "pouvoir", + "prairie", + "premier", + "prendre", + "presque", + "preuve", + "prier", + "primeur", + "prince", + "prison", + "priver", + "prix", + "prochain", + "produire", + "profond", + "proie", + "projet", + "promener", + "prononcer", + "propre", + "prose", + "prouver", + "prune", + "public", + "puce", + "pudeur", + "puiser", + "pull", + "pulpe", + "puma", + "punir", + "purge", + "putois", + "quand", + "quartier", + "quasi", + "quatre", + "quel", + "question", + "queue", + "quiche", + "quille", + "quinze", + "quitter", + "quoi", + "rabais", + "raboter", + "race", + "racheter", + "racine", + "racler", + "raconter", + "radar", + "radio", + "rafale", + "rage", + "ragot", + "raideur", + "raie", + "rail", + "raison", + "ramasser", + "ramener", + "rampe", + "rance", + "rang", + "rapace", + "rapide", + "rapport", + "rarement", + "rasage", + "raser", + "rasoir", + "rassurer", + "rater", + "ratio", + "rature", + "ravage", + "ravir", + "rayer", + "rayon", + "rebond", + "recevoir", + "recherche", + "record", + "reculer", + "redevenir", + "refuser", + "regard", + "regretter", + "rein", + "rejeter", + "rejoindre", + "relation", + "relever", + "religion", + "remarquer", + "remettre", + "remise", + "remonter", + "remplir", + "remuer", + "rencontre", + "rendre", + "renier", + "renoncer", + "rentrer", + "renverser", + "repas", + "repli", + "reposer", + "reproche", + "requin", + "respect", + "ressembler", + "reste", + "retard", + "retenir", + "retirer", + "retour", + "retrouver", + "revenir", + "revoir", + "revue", + "rhume", + "ricaner", + "riche", + "rideau", + "ridicule", + "rien", + "rigide", + "rincer", + "rire", + "risquer", + "rituel", + "rivage", + "rive", + "robe", + "robot", + "robuste", + "rocade", + "roche", + "rodeur", + "rogner", + "roman", + "rompre", + "ronce", + "rondeur", + "ronger", + "roque", + "rose", + "rosir", + "rotation", + "rotule", + "roue", + "rouge", + "rouler", + "route", + "ruban", + "rubis", + "ruche", + "rude", + "ruelle", + "ruer", + "rugby", + "rugir", + "ruine", + "rumeur", + "rural", + "ruse", + "rustre", + "sable", + "sabot", + "sabre", + "sacre", + "sage", + "saint", + "saisir", + "salade", + "salive", + "salle", + "salon", + "salto", + "salut", + "salve", + "samba", + "sandale", + "sanguin", + "sapin", + "sarcasme", + "satisfaire", + "sauce", + "sauf", + "sauge", + "saule", + "sauna", + "sauter", + "sauver", + "savoir", + "science", + "scoop", + "score", + "second", + "secret", + "secte", + "seigneur", + "sein", + "seize", + "selle", + "selon", + "semaine", + "sembler", + "semer", + "semis", + "sensuel", + "sentir", + "sept", + "serpe", + "serrer", + "sertir", + "service", + "seuil", + "seulement", + "short", + "sien", + "sigle", + "signal", + "silence", + "silo", + "simple", + "singe", + "sinon", + "sinus", + "sioux", + "sirop", + "site", + "situation", + "skier", + "snob", + "sobre", + "social", + "socle", + "sodium", + "soigner", + "soir", + "soixante", + "soja", + "solaire", + "soldat", + "soleil", + "solide", + "solo", + "solvant", + "sombre", + "somme", + "somnoler", + "sondage", + "songeur", + "sonner", + "sorte", + "sosie", + "sottise", + "souci", + "soudain", + "souffrir", + "souhaiter", + "soulever", + "soumettre", + "soupe", + "sourd", + "soustraire", + "soutenir", + "souvent", + "soyeux", + "spectacle", + "sport", + "stade", + "stagiaire", + "stand", + "star", + "statue", + "stock", + "stop", + "store", + "style", + "suave", + "subir", + "sucre", + "suer", + "suffire", + "suie", + "suite", + "suivre", + "sujet", + "sulfite", + "supposer", + "surf", + "surprendre", + "surtout", + "surveiller", + "tabac", + "table", + "tabou", + "tache", + "tacler", + "tacot", + "tact", + "taie", + "taille", + "taire", + "talon", + "talus", + "tandis", + "tango", + "tanin", + "tant", + "taper", + "tapis", + "tard", + "tarif", + "tarot", + "tarte", + "tasse", + "taureau", + "taux", + "taverne", + "taxer", + "taxi", + "tellement", + "temple", + "tendre", + "tenir", + "tenter", + "tenu", + "terme", + "ternir", + "terre", + "test", + "texte", + "thym", + "tibia", + "tiers", + "tige", + "tipi", + "tique", + "tirer", + "tissu", + "titre", + "toast", + "toge", + "toile", + "toiser", + "toiture", + "tomber", + "tome", + "tonne", + "tonte", + "toque", + "torse", + "tortue", + "totem", + "toucher", + "toujours", + "tour", + "tousser", + "tout", + "toux", + "trace", + "train", + "trame", + "tranquille", + "travail", + "trembler", + "trente", + "tribu", + "trier", + "trio", + "tripe", + "triste", + "troc", + "trois", + "tromper", + "tronc", + "trop", + "trotter", + "trouer", + "truc", + "truite", + "tuba", + "tuer", + "tuile", + "turbo", + "tutu", + "tuyau", + "type", + "union", + "unique", + "unir", + "unisson", + "untel", + "urne", + "usage", + "user", + "usiner", + "usure", + "utile", + "vache", + "vague", + "vaincre", + "valeur", + "valoir", + "valser", + "valve", + "vampire", + "vaseux", + "vaste", + "veau", + "veille", + "veine", + "velours", + "velu", + "vendre", + "venir", + "vent", + "venue", + "verbe", + "verdict", + "version", + "vertige", + "verve", + "veste", + "veto", + "vexer", + "vice", + "victime", + "vide", + "vieil", + "vieux", + "vigie", + "vigne", + "ville", + "vingt", + "violent", + "virer", + "virus", + "visage", + "viser", + "visite", + "visuel", + "vitamine", + "vitrine", + "vivant", + "vivre", + "vocal", + "vodka", + "vogue", + "voici", + "voile", + "voir", + "voisin", + "voiture", + "volaille", + "volcan", + "voler", + "volt", + "votant", + "votre", + "vouer", + "vouloir", + "vous", + "voyage", + "voyou", + "vrac", + "vrai", + "yacht", + "yeti", + "yeux", + "yoga", + "zeste", + "zinc", + "zone", + "zoom" + }), 4) + { + populate_maps(); + } + }; +} + +#endif diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h index 675531453..1a1e6b467 100644 --- a/src/mnemonics/german.h +++ b/src/mnemonics/german.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor Shrikez
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language class German: public Base
{
public:
- German()
- {
- word_list = new std::vector<std::string>({
+ German(): Base("German", std::vector<std::string>({
"Abakus",
"Abart",
"abbilden",
@@ -1680,11 +1678,8 @@ namespace Language "Zündung",
"Zweck",
"Zyklop"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "German";
+ }), 4) + { populate_maps();
}
};
diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h index 08852a97b..28cee9d9a 100644 --- a/src/mnemonics/italian.h +++ b/src/mnemonics/italian.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor Shrikez
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language class Italian: public Base
{
public:
- Italian()
- {
- word_list = new std::vector<std::string>({
+ Italian(): Base("Italian", std::vector<std::string>({
"abbinare",
"abbonato",
"abisso",
@@ -1680,11 +1678,8 @@ namespace Language "zolfo",
"zombie",
"zucchero"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Italian";
+ }), 4) + { populate_maps();
}
};
diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h index 421188893..d5ab4bc84 100644 --- a/src/mnemonics/japanese.h +++ b/src/mnemonics/japanese.h @@ -1,23 +1,43 @@ -// Word list originally created by dabura667
-//
-// Copyright (c) 2014-2016, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -51,9 +71,7 @@ namespace Language class Japanese: public Base
{
public:
- Japanese()
- {
- word_list = new std::vector<std::string>({
+ Japanese(): Base("Japanese", std::vector<std::string>({
"あいこくしん",
"あいさつ",
"あいだ",
@@ -1680,11 +1698,8 @@ namespace Language "ひさん",
"びじゅつかん",
"ひしょ"
- });
- unique_prefix_length = 3;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Japanese";
+ }), 3)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h index e0297c847..8f0a7a9d3 100644 --- a/src/mnemonics/language_base.h +++ b/src/mnemonics/language_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -38,6 +38,7 @@ #include <vector>
#include <unordered_map>
#include <string>
+#include "misc_log_ex.h"
/*!
* \namespace Language
@@ -73,44 +74,62 @@ namespace Language class Base
{
protected:
- std::vector<std::string> *word_list; /*!< A pointer to the array of words */
- std::unordered_map<std::string, uint32_t> *word_map; /*!< hash table to find word's index */
- std::unordered_map<std::string, uint32_t> *trimmed_word_map; /*!< hash table to find word's trimmed index */
+ enum {
+ ALLOW_SHORT_WORDS = 1<<0,
+ ALLOW_DUPLICATE_PREFIXES = 1<<1,
+ };
+ const std::vector<std::string> word_list; /*!< A pointer to the array of words */
+ std::unordered_map<std::string, uint32_t> word_map; /*!< hash table to find word's index */
+ std::unordered_map<std::string, uint32_t> trimmed_word_map; /*!< hash table to find word's trimmed index */
std::string language_name; /*!< Name of language */
uint32_t unique_prefix_length; /*!< Number of unique starting characters to trim the wordlist to when matching */
/*!
* \brief Populates the word maps after the list is ready.
*/
- void populate_maps()
+ void populate_maps(uint32_t flags = 0)
{
int ii;
- std::vector<std::string>::iterator it;
- for (it = word_list->begin(), ii = 0; it != word_list->end(); it++, ii++)
+ std::vector<std::string>::const_iterator it;
+ if (word_list.size () != 1626)
+ throw std::runtime_error("Wrong word list length for " + language_name);
+ for (it = word_list.begin(), ii = 0; it != word_list.end(); it++, ii++)
{
- (*word_map)[*it] = ii;
+ word_map[*it] = ii;
+ if ((*it).size() < unique_prefix_length)
+ {
+ if (flags & ALLOW_SHORT_WORDS)
+ MWARNING(language_name << " word '" << *it << "' is shorter than its prefix length, " << unique_prefix_length);
+ else
+ throw std::runtime_error("Too short word in " + language_name + " word list: " + *it);
+ }
+ std::string trimmed;
if (it->length() > unique_prefix_length)
{
- (*trimmed_word_map)[utf8prefix(*it, unique_prefix_length)] = ii;
+ trimmed = utf8prefix(*it, unique_prefix_length);
}
else
{
- (*trimmed_word_map)[*it] = ii;
+ trimmed = *it;
+ }
+ if (trimmed_word_map.find(trimmed) != trimmed_word_map.end())
+ {
+ if (flags & ALLOW_DUPLICATE_PREFIXES)
+ MWARNING("Duplicate prefix in " << language_name << " word list: " << trimmed);
+ else
+ throw std::runtime_error("Duplicate prefix in " + language_name + " word list: " + trimmed);
}
+ trimmed_word_map[trimmed] = ii;
}
}
public:
- Base()
+ Base(const char *language_name, const std::vector<std::string> &words, uint32_t prefix_length):
+ word_list(words),
+ unique_prefix_length(prefix_length),
+ language_name(language_name)
{
- word_list = new std::vector<std::string>;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- unique_prefix_length = 4;
}
virtual ~Base()
{
- delete word_list;
- delete word_map;
- delete trimmed_word_map;
}
/*!
* \brief Returns a pointer to the word list.
@@ -118,7 +137,7 @@ namespace Language */
const std::vector<std::string>& get_word_list() const
{
- return *word_list;
+ return word_list;
}
/*!
* \brief Returns a pointer to the word map.
@@ -126,7 +145,7 @@ namespace Language */
const std::unordered_map<std::string, uint32_t>& get_word_map() const
{
- return *word_map;
+ return word_map;
}
/*!
* \brief Returns a pointer to the trimmed word map.
@@ -134,13 +153,13 @@ namespace Language */
const std::unordered_map<std::string, uint32_t>& get_trimmed_word_map() const
{
- return *trimmed_word_map;
+ return trimmed_word_map;
}
/*!
* \brief Returns the name of the language.
* \return Name of the language.
*/
- std::string get_language_name() const
+ const std::string &get_language_name() const
{
return language_name;
}
diff --git a/src/mnemonics/old_english.h b/src/mnemonics/old_english.h index cd4aa7629..21ac95de3 100644 --- a/src/mnemonics/old_english.h +++ b/src/mnemonics/old_english.h @@ -1,6 +1,6 @@ // Word list originally created as part of the Electrum project, Copyright (C) 2014 Thomas Voegtlin
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language class OldEnglish: public Base
{
public:
- OldEnglish()
- {
- word_list = new std::vector<std::string>({
+ OldEnglish(): Base("OldEnglish", std::vector<std::string>({
"like",
"just",
"love",
@@ -1680,12 +1678,9 @@ namespace Language "unseen",
"weapon",
"weary"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "OldEnglish";
- populate_maps();
+ }), 4)
+ {
+ populate_maps(ALLOW_DUPLICATE_PREFIXES | ALLOW_SHORT_WORDS);
}
};
}
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h index 782a2ad5d..f9d66afc4 100644 --- a/src/mnemonics/portuguese.h +++ b/src/mnemonics/portuguese.h @@ -1,21 +1,44 @@ -// Copyright (c) 2014-2016, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
+//
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -49,9 +72,7 @@ namespace Language class Portuguese: public Base
{
public:
- Portuguese()
- {
- word_list = new std::vector<std::string>({
+ Portuguese(): Base("Portuguese", std::vector<std::string>({
"abaular",
"abdominal",
"abeto",
@@ -1678,11 +1699,8 @@ namespace Language "zeloso",
"zenite",
"zumbi"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Portuguese";
+ }), 4)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h index 6d98ca4b8..b3db4aa4c 100644 --- a/src/mnemonics/russian.h +++ b/src/mnemonics/russian.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor sammy007
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language class Russian: public Base
{
public:
- Russian()
- {
- word_list = new std::vector<std::string>({
+ Russian(): Base("Russian", std::vector<std::string>({
"абажур",
"абзац",
"абонент",
@@ -1680,11 +1678,8 @@ namespace Language "яхта",
"ячейка",
"ящик"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Russian";
+ }), 4) + { populate_maps();
}
};
diff --git a/src/mnemonics/singleton.h b/src/mnemonics/singleton.h index 48b217a0c..5ba9269b1 100644 --- a/src/mnemonics/singleton.h +++ b/src/mnemonics/singleton.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h index 7275f4c42..117890ada 100644 --- a/src/mnemonics/spanish.h +++ b/src/mnemonics/spanish.h @@ -1,23 +1,44 @@ -// Word list originally created as part of the Electrum project, Copyright (C) 2014 Thomas Voegtlin
-//
-// Copyright (c) 2014-2016, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is Copyright (c) 2014-2017, The Monero Project
+//
// All rights reserved.
-//
+//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
-//
+//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
-//
+//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
-//
+//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
-//
+//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -51,9 +72,7 @@ namespace Language class Spanish: public Base
{
public:
- Spanish()
- {
- word_list = new std::vector<std::string>({
+ Spanish(): Base("Spanish", std::vector<std::string>({
"ábaco",
"abdomen",
"abeja",
@@ -1680,14 +1699,11 @@ namespace Language "risa",
"ritmo",
"rito"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Spanish";
- populate_maps();
+ }), 4)
+ {
+ populate_maps(ALLOW_SHORT_WORDS);
}
};
}
-#endif +#endif
diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt index b5b7d87ff..0704940b5 100644 --- a/src/p2p/CMakeLists.txt +++ b/src/p2p/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -38,6 +38,7 @@ source_group(p2p FILES ${P2P}) monero_add_library(p2p ${P2P}) target_link_libraries(p2p PUBLIC + epee ${UPNP_LIBRARIES} ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff --git a/src/p2p/connection_basic.cpp b/src/p2p/connection_basic.cpp index bdb239ca8..df34379e2 100644 --- a/src/p2p/connection_basic.cpp +++ b/src/p2p/connection_basic.cpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief base for connection, contains e.g. the ratelimit hooks -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/connection_basic.hpp b/src/p2p/connection_basic.hpp index 5452fa6fb..bea2df1cd 100644 --- a/src/p2p/connection_basic.hpp +++ b/src/p2p/connection_basic.hpp @@ -8,7 +8,7 @@ // ! (how ever if in some wonderful juristdictions that is not the case, then why not make another sub-class withat that members and licence it as epee part) // ! Working on above premise, IF this is valid in your juristdictions, then consider this code as released as: -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 51ab9b3f2..13cd3f5b0 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -112,7 +112,7 @@ namespace nodetool if (ver == 0) { // from v1, we do not store the peer id anymore - peerid_type peer_id; + peerid_type peer_id = AUTO_VAL_INIT (peer_id); a & peer_id; } } @@ -193,7 +193,6 @@ namespace nodetool bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); bool handle_command_line( const boost::program_options::variables_map& vm - , bool testnet ); bool idle_worker(); bool handle_remote_peerlist(const std::list<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context); @@ -222,6 +221,7 @@ namespace nodetool void cache_connect_fail_info(const net_address& addr); bool is_addr_recently_failed(const net_address& addr); bool is_priority_node(const net_address& na); + std::set<std::string> get_seed_nodes(bool testnet) const; template <class Container> bool connect_to_peerlist(const Container& peers); @@ -325,6 +325,8 @@ namespace nodetool epee::critical_section m_ip_fails_score_lock; std::map<uint32_t, uint64_t> m_ip_fails_score; + + bool m_testnet; }; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 568b8208e..5c903b1f5 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -65,6 +65,7 @@ #define NET_MAKE_IP(b1,b2,b3,b4) ((LPARAM)(((DWORD)(b1)<<24)+((DWORD)(b2)<<16)+((DWORD)(b3)<<8)+((DWORD)(b4)))) +#define MIN_WANTED_SEED_NODES 12 namespace nodetool { @@ -108,8 +109,8 @@ namespace nodetool void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc) { command_line::add_arg(desc, arg_p2p_bind_ip); - command_line::add_arg(desc, arg_p2p_bind_port); - command_line::add_arg(desc, arg_testnet_p2p_bind_port); + command_line::add_arg(desc, arg_p2p_bind_port, false); + command_line::add_arg(desc, arg_testnet_p2p_bind_port, false); command_line::add_arg(desc, arg_p2p_external_port); command_line::add_arg(desc, arg_p2p_allow_local_ip); command_line::add_arg(desc, arg_p2p_add_peer); @@ -287,10 +288,9 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::handle_command_line( const boost::program_options::variables_map& vm - , bool testnet ) { - auto p2p_bind_arg = testnet ? arg_testnet_p2p_bind_port : arg_p2p_bind_port; + auto p2p_bind_arg = m_testnet ? arg_testnet_p2p_bind_port : arg_p2p_bind_port; m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip); m_port = command_line::get_arg(vm, p2p_bind_arg); @@ -309,7 +309,7 @@ namespace nodetool bool r = parse_peer_from_string(pe.adr, pr_str); CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); if (pe.adr.port == 0) - pe.adr.port = testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; + pe.adr.port = m_testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; m_command_line_peers.push_back(pe); } } @@ -398,17 +398,42 @@ namespace nodetool //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + std::set<std::string> node_server<t_payload_net_handler>::get_seed_nodes(bool testnet) const + { + std::set<std::string> full_addrs; + if (testnet) + { + full_addrs.insert("212.83.175.67:28080"); + full_addrs.insert("5.9.100.248:28080"); + full_addrs.insert("163.172.182.165:28080"); + full_addrs.insert("195.154.123.123:28080"); + full_addrs.insert("212.83.172.165:28080"); + } + else + { + full_addrs.insert("107.152.130.98:18080"); + full_addrs.insert("212.83.175.67:18080"); + full_addrs.insert("5.9.100.248:18080"); + full_addrs.insert("163.172.182.165:18080"); + full_addrs.insert("161.67.132.39:18080"); + full_addrs.insert("198.74.231.92:18080"); + full_addrs.insert("195.154.123.123:28080"); + full_addrs.insert("212.83.172.165:28080"); + } + return full_addrs; + } + + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::init(const boost::program_options::variables_map& vm) { std::set<std::string> full_addrs; - bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on); - if (testnet) + if (m_testnet) { memcpy(&m_network_id, &::config::testnet::NETWORK_ID, 16); - full_addrs.insert("163.172.182.165:28080"); - full_addrs.insert("204.12.248.66:28080"); - full_addrs.insert("5.9.100.248:28080"); + full_addrs = get_seed_nodes(true); } else { @@ -481,14 +506,16 @@ namespace nodetool ++i; } - if (!full_addrs.size()) + // append the fallback nodes if we have too few seed nodes to start with + if (full_addrs.size() < MIN_WANTED_SEED_NODES) { - MINFO("DNS seed node lookup either timed out or failed, falling back to defaults"); - full_addrs.insert("198.74.231.92:18080"); - full_addrs.insert("161.67.132.39:18080"); - full_addrs.insert("163.172.182.165:18080"); - full_addrs.insert("204.12.248.66:18080"); - full_addrs.insert("5.9.100.248:18080"); + if (full_addrs.empty()) + MINFO("DNS seed node lookup either timed out or failed, falling back to defaults"); + else + MINFO("Not enough DNS seed nodes found, using fallback defaults too"); + + for (const auto &peer: get_seed_nodes(false)) + full_addrs.insert(peer); } } @@ -499,14 +526,14 @@ namespace nodetool } MDEBUG("Number of seed nodes: " << m_seed_nodes.size()); - bool res = handle_command_line(vm, testnet); + bool res = handle_command_line(vm); CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); - auto config_arg = testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; + auto config_arg = m_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir; m_config_folder = command_line::get_arg(vm, config_arg); - if ((!testnet && m_port != std::to_string(::config::P2P_DEFAULT_PORT)) - || (testnet && m_port != std::to_string(::config::testnet::P2P_DEFAULT_PORT))) { + if ((!m_testnet && m_port != std::to_string(::config::P2P_DEFAULT_PORT)) + || (m_testnet && m_port != std::to_string(::config::testnet::P2P_DEFAULT_PORT))) { m_config_folder = m_config_folder + "/" + m_port; } @@ -1083,7 +1110,14 @@ namespace nodetool while(rand_count < (max_random_index+1)*3 && try_count < 10 && !m_net_server.is_stop_signal_sent()) { ++rand_count; - size_t random_index = get_random_index_with_fixed_probability(max_random_index); + size_t random_index; + + if (use_white_list) { + random_index = get_random_index_with_fixed_probability(max_random_index); + } else { + random_index = crypto::rand<size_t>() % m_peerlist.get_gray_peers_count(); + } + CHECK_AND_ASSERT_MES(random_index < local_peers_count, false, "random_starter_index < peers_local.size() failed!!"); if(tried_peers.count(random_index)) @@ -1135,6 +1169,7 @@ namespace nodetool { size_t try_count = 0; size_t current_index = crypto::rand<size_t>()%m_seed_nodes.size(); + bool fallback_nodes_added = false; while(true) { if(m_net_server.is_stop_signal_sent()) @@ -1144,8 +1179,22 @@ namespace nodetool break; if(++try_count > m_seed_nodes.size()) { - MWARNING("Failed to connect to any of seed peers, continuing without seeds"); - break; + if (!fallback_nodes_added) + { + MWARNING("Failed to connect to any of seed peers, trying fallback seeds"); + for (const auto &peer: get_seed_nodes(m_testnet)) + { + MDEBUG("Fallback seed node: " << peer); + append_net_address(m_seed_nodes, peer); + } + fallback_nodes_added = true; + // continue for another few cycles + } + else + { + MWARNING("Failed to connect to any of seed peers, continuing without seeds"); + break; + } } if(++current_index >= m_seed_nodes.size()) current_index = 0; @@ -1734,7 +1783,6 @@ namespace nodetool bool node_server<t_payload_net_handler>::parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor<std::vector<std::string> > & arg, Container& container) { std::vector<std::string> perrs = command_line::get_arg(vm, arg); - bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on); for(const std::string& pr_str: perrs) { @@ -1742,7 +1790,7 @@ namespace nodetool bool r = parse_peer_from_string(na, pr_str); CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); if (na.port == 0) - na.port = testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; + na.port = m_testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT; container.push_back(na); } diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 69bee890c..6aba7aa24 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 9af3694b6..de0f51cc3 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -446,13 +446,10 @@ namespace nodetool return false; } - size_t x = crypto::rand<size_t>() % (m_peers_gray.size() + 1); - size_t res = (x * x * x) / (m_peers_gray.size() * m_peers_gray.size()); //parabola \/ - - LOG_PRINT_L3("Random gray peer index=" << res << "(x="<< x << ", max_index=" << m_peers_gray.size() << ")"); + size_t random_index = crypto::rand<size_t>() % m_peers_gray.size(); peers_indexed::index<by_time>::type& by_time_index = m_peers_gray.get<by_time>(); - pe = *epee::misc_utils::move_it_backward(--by_time_index.end(), res); + pe = *epee::misc_utils::move_it_backward(--by_time_index.end(), random_index); return true; diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index 9a0d12c2e..6891ac80c 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/network_throttle-detail.cpp b/src/p2p/network_throttle-detail.cpp index d4fe356a9..0747b6f36 100644 --- a/src/p2p/network_throttle-detail.cpp +++ b/src/p2p/network_throttle-detail.cpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief implementaion for throttling of connection (count and rate-limit speed etc) -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -216,7 +216,7 @@ void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_s std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends; std::string history_str = oss.str(); - MDEBUG("Throttle " << m_name << ": packet of ~"<<packet_size<<"b " << " (from "<<orginal_size<<" b)" + MTRACE("Throttle " << m_name << ": packet of ~"<<packet_size<<"b " << " (from "<<orginal_size<<" b)" << " Speed AVG=" << std::setw(4) << ((long int)(cts .average/1024)) <<"[w="<<cts .window<<"]" << " " << std::setw(4) << ((long int)(cts2.average/1024)) <<"[w="<<cts2.window<<"]" <<" / " << " Limit="<< ((long int)(m_target_speed/1024)) <<" KiB/sec " @@ -306,7 +306,7 @@ void network_throttle::calculate_times(size_t packet_size, calculate_times_struc if (dbg) { std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends; std::string history_str = oss.str(); - MDEBUG((cts.delay > 0 ? "SLEEP" : "") + MTRACE((cts.delay > 0 ? "SLEEP" : "") << "dbg " << m_name << ": " << "speed is A=" << std::setw(8) <<cts.average<<" vs " << "Max=" << std::setw(8) <<M<<" " diff --git a/src/p2p/network_throttle-detail.hpp b/src/p2p/network_throttle-detail.hpp index ef7227eb9..c514db450 100644 --- a/src/p2p/network_throttle-detail.hpp +++ b/src/p2p/network_throttle-detail.hpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief implementaion for throttling of connection (count and rate-limit speed etc) -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/network_throttle.cpp b/src/p2p/network_throttle.cpp index 30538bb3c..6d68f3286 100644 --- a/src/p2p/network_throttle.cpp +++ b/src/p2p/network_throttle.cpp @@ -26,7 +26,7 @@ Throttling work by: */ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/network_throttle.hpp b/src/p2p/network_throttle.hpp index 4f6cbe9cf..a747cdd71 100644 --- a/src/p2p/network_throttle.hpp +++ b/src/p2p/network_throttle.hpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief interface for throttling of connection (count and rate-limit speed etc) -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index dc738d92c..5ec012714 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/p2p/stdafx.h b/src/p2p/stdafx.h index 77daa484d..5e9baa895 100644 --- a/src/p2p/stdafx.h +++ b/src/p2p/stdafx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/platform/mingw/alloca.h b/src/platform/mingw/alloca.h index 131ad3487..f850d1cdf 100644 --- a/src/platform/mingw/alloca.h +++ b/src/platform/mingw/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/alloca.h b/src/platform/msc/alloca.h index 0007d32db..2f3f79a3f 100644 --- a/src/platform/msc/alloca.h +++ b/src/platform/msc/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/inline_c.h b/src/platform/msc/inline_c.h index f28d70733..030604ef8 100644 --- a/src/platform/msc/inline_c.h +++ b/src/platform/msc/inline_c.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/stdbool.h b/src/platform/msc/stdbool.h index 22e34c24a..05d2e7a38 100644 --- a/src/platform/msc/stdbool.h +++ b/src/platform/msc/stdbool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/sys/param.h b/src/platform/msc/sys/param.h index 4201c8784..db6eca28b 100644 --- a/src/platform/msc/sys/param.h +++ b/src/platform/msc/sys/param.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt index 334a7f350..e15af205a 100644 --- a/src/ringct/CMakeLists.txt +++ b/src/ringct/CMakeLists.txt @@ -49,5 +49,6 @@ target_link_libraries(ringct PUBLIC common crypto + cryptonote_basic PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/ringct/rctCryptoOps.c b/src/ringct/rctCryptoOps.c index 9bb9a6891..f69d692f6 100644 --- a/src/ringct/rctCryptoOps.c +++ b/src/ringct/rctCryptoOps.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctCryptoOps.h b/src/ringct/rctCryptoOps.h index 58c6964d8..2674c2243 100644 --- a/src/ringct/rctCryptoOps.h +++ b/src/ringct/rctCryptoOps.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 0e1715072..d0e0964b6 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -36,6 +36,8 @@ using namespace std; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "ringct" +#define CHECK_AND_ASSERT_THROW_MES_L1(expr, message) {if(!(expr)) {MWARNING(message); throw std::runtime_error(message);}} + namespace rct { //Various key initialization functions @@ -175,7 +177,7 @@ namespace rct { void scalarmultKey(key & aP, const key &P, const key &a) { ge_p3 A; ge_p2 R; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_scalarmult(&R, a.bytes, &A); ge_tobytes(aP.bytes, &R); } @@ -184,7 +186,7 @@ namespace rct { key scalarmultKey(const key & P, const key & a) { ge_p3 A; ge_p2 R; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_scalarmult(&R, a.bytes, &A); key aP; ge_tobytes(aP.bytes, &R); @@ -196,7 +198,7 @@ namespace rct { key scalarmultH(const key & a) { ge_p3 A; ge_p2 R; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, H.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, H.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_scalarmult(&R, a.bytes, &A); key aP; ge_tobytes(aP.bytes, &R); @@ -208,8 +210,8 @@ namespace rct { //for curve points: AB = A + B void addKeys(key &AB, const key &A, const key &B) { ge_p3 B2, A2; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_cached tmp2; ge_p3_to_cached(&tmp2, &B2); ge_p1p1 tmp3; @@ -231,7 +233,7 @@ namespace rct { void addKeys2(key &aGbB, const key &a, const key &b, const key & B) { ge_p2 rv; ge_p3 B2; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_double_scalarmult_base_vartime(&rv, b.bytes, &B2, a.bytes); ge_tobytes(aGbB.bytes, &rv); } @@ -240,7 +242,7 @@ namespace rct { // input B a curve point and output a ge_dsmp which has precomputation applied void precomp(ge_dsmp rv, const key & B) { ge_p3 B2; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_dsm_precomp(rv, &B2); } @@ -250,7 +252,7 @@ namespace rct { void addKeys3(key &aAbB, const key &a, const key &A, const key &b, const ge_dsmp B) { ge_p2 rv; ge_p3 A2; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_double_scalarmult_precomp_vartime(&rv, a.bytes, &A2, b.bytes, B); ge_tobytes(aAbB.bytes, &rv); } @@ -260,8 +262,8 @@ namespace rct { //AB = A - B where A, B are curve points void subKeys(key & AB, const key &A, const key &B) { ge_p3 B2, A2; - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_cached tmp2; ge_p3_to_cached(&tmp2, &B2); ge_p1p1 tmp3; @@ -381,7 +383,7 @@ namespace rct { ge_p2 point; ge_p3 res; key h = cn_fast_hash(hh); - CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&res, h.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); + CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&res, h.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__)); ge_p3_to_p2(&point, &res); ge_mul8(&point2, &point); ge_p1p1_to_p3(&res, &point2); diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 90f54b050..cb19bbbd6 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -66,6 +66,7 @@ namespace rct { static const key Z = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; static const key I = { {0x01, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; + static const key L = { {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } }; //Creates a zero scalar inline key zero() { return Z; } @@ -73,6 +74,9 @@ namespace rct { //Creates a zero elliptic curve point inline key identity() { return I; } inline void identity(key &Id) { memcpy(&Id, &I, 32); } + //Creates a key equal to the curve order + inline key curveOrder() { return L; } + inline void curveOrder(key &l) { l = L; } //copies a scalar or point inline void copy(key &AA, const key &A) { memcpy(&AA, &A, 32); } inline key copy(const key & A) { key AA; memcpy(&AA, &A, 32); return AA; } diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index b8f3596e8..8efd6a07c 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -34,7 +34,7 @@ #include "common/thread_group.h" #include "common/util.h" #include "rctSigs.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" using namespace crypto; using namespace std; @@ -873,8 +873,7 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) { CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig"); - CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs"); - CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); //mask amount and mask @@ -902,8 +901,7 @@ namespace rct { xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) { CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig"); - CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs"); - CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); //mask amount and mask diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 0c27745e1..c820fb297 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -47,6 +47,8 @@ extern "C" { #include "crypto/generic-ops.h" #include "crypto/crypto.h" +#include "hex.h" +#include "span.h" #include "serialization/serialization.h" #include "serialization/debug_archive.h" #include "serialization/binary_archive.h" @@ -443,8 +445,9 @@ namespace cryptonote { static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); } } -template<typename T> std::ostream &print256(std::ostream &o, const T &v); -inline std::ostream &operator <<(std::ostream &o, const rct::key &v) { return print256(o, v); } +inline std::ostream &operator <<(std::ostream &o, const rct::key &v) { + epee::to_hex::formatted(o, epee::as_byte_span(v)); return o; +} BLOB_SERIALIZER(rct::key); diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 6df93cde1..f6037d7e3 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -27,9 +27,11 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(rpc_sources - core_rpc_server.cpp) + core_rpc_server.cpp + rpc_args.cpp) -set(rpc_headers) +set(rpc_headers + rpc_args.h) set(rpc_private_headers core_rpc_server.h @@ -44,9 +46,11 @@ monero_add_library(rpc ${rpc_private_headers}) target_link_libraries(rpc PUBLIC + common cryptonote_core cryptonote_protocol epee + ${Boost_REGEX_LIBRARY} ${Boost_THREAD_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index b2e8e6716..76a69fbf6 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,13 +33,20 @@ using namespace epee; #include "core_rpc_server.h" #include "common/command_line.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "common/updates.h" +#include "common/download.h" +#include "common/util.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "misc_language.h" #include "crypto/hash.h" +#include "rpc/rpc_args.h" #include "core_rpc_server_error_codes.h" +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc" + #define MAX_RESTRICTED_FAKE_OUTS_COUNT 40 #define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 500 @@ -49,11 +56,10 @@ namespace cryptonote //----------------------------------------------------------------------------------- void core_rpc_server::init_options(boost::program_options::options_description& desc) { - command_line::add_arg(desc, arg_rpc_bind_ip); command_line::add_arg(desc, arg_rpc_bind_port); command_line::add_arg(desc, arg_testnet_rpc_bind_port); command_line::add_arg(desc, arg_restricted_rpc); - command_line::add_arg(desc, arg_user_agent); + cryptonote::rpc_args::init_options(desc); } //------------------------------------------------------------------------------------------------------------------------------ core_rpc_server::core_rpc_server( @@ -64,29 +70,29 @@ namespace cryptonote , m_p2p(p2p) {} //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::handle_command_line( + bool core_rpc_server::init( const boost::program_options::variables_map& vm ) { + m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on); + m_net_server.set_threads_prefix("RPC"); + auto p2p_bind_arg = m_testnet ? arg_testnet_rpc_bind_port : arg_rpc_bind_port; - m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); - m_port = command_line::get_arg(vm, p2p_bind_arg); + auto rpc_config = cryptonote::rpc_args::process(vm); + if (!rpc_config) + return false; + m_restricted = command_line::get_arg(vm, arg_restricted_rpc); - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::init( - const boost::program_options::variables_map& vm - ) - { - m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on); - std::string m_user_agent = command_line::get_arg(vm, command_line::arg_user_agent); - m_net_server.set_threads_prefix("RPC"); - bool r = handle_command_line(vm); - CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); - return epee::http_server_impl_base<core_rpc_server, connection_context>::init(m_port, m_bind_ip, m_user_agent); + boost::optional<epee::net_utils::http::login> http_login{}; + std::string port = command_line::get_arg(vm, p2p_bind_arg); + if (rpc_config->login) + http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()); + + return epee::http_server_impl_base<core_rpc_server, connection_context>::init( + std::move(port), std::move(rpc_config->bind_ip), std::move(http_login) + ); } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::check_core_busy() @@ -148,10 +154,27 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + static cryptonote::blobdata get_pruned_tx_blob(const cryptonote::blobdata &blobdata) + { + cryptonote::transaction tx; + + if (!cryptonote::parse_and_validate_tx_from_blob(blobdata, tx)) + { + MERROR("Failed to parse and validate tx from blob"); + return blobdata; + } + + std::stringstream ss; + binary_archive<true> ba(ss); + bool r = tx.serialize_base(ba); + CHECK_AND_ASSERT_MES(r, blobdata, "Failed to serialize rct signatures base"); + return ss.str(); + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { CHECK_CORE_BUSY(); - std::list<std::pair<block, std::list<transaction> > > bs; + std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > > bs; if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { @@ -159,24 +182,39 @@ namespace cryptonote return false; } - for(auto& b: bs) + size_t pruned_size = 0, unpruned_size = 0, ntxes = 0; + for(auto& bd: bs) { res.blocks.resize(res.blocks.size()+1); - res.blocks.back().block = block_to_blob(b.first); + res.blocks.back().block = bd.first; + pruned_size += bd.first.size(); + unpruned_size += bd.first.size(); res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices()); res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); - bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.first.miner_tx), res.output_indices.back().indices.back().indices); + block b; + if (!parse_and_validate_block_from_blob(bd.first, b)) + { + res.status = "Invalid block"; + return false; + } + bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.miner_tx), res.output_indices.back().indices.back().indices); if (!r) { res.status = "Failed"; return false; } size_t txidx = 0; - for(auto& t: b.second) + ntxes += bd.second.size(); + for(const auto& t: bd.second) { - res.blocks.back().txs.push_back(tx_to_blob(t)); + if (req.prune) + res.blocks.back().txs.push_back(get_pruned_tx_blob(t)); + else + res.blocks.back().txs.push_back(t); + pruned_size += res.blocks.back().txs.back().size(); + unpruned_size += t.size(); res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices()); - bool r = m_core.get_tx_outputs_gindexs(b.first.tx_hashes[txidx++], res.output_indices.back().indices.back().indices); + bool r = m_core.get_tx_outputs_gindexs(b.tx_hashes[txidx++], res.output_indices.back().indices.back().indices); if (!r) { res.status = "Failed"; @@ -185,6 +223,7 @@ namespace cryptonote } } + MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, pruned size " << pruned_size << ", unpruned size " << unpruned_size); res.status = CORE_RPC_STATUS_OK; return true; } @@ -635,7 +674,7 @@ namespace cryptonote boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); - if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs)) + if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery)) { res.status = "Failed, mining not started"; LOG_PRINT_L0(res.status); @@ -659,10 +698,11 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res) { - CHECK_CORE_READY(); + CHECK_CORE_BUSY(); const miner& lMiner = m_core.get_miner(); res.active = lMiner.is_mining(); + res.is_background_mining_enabled = lMiner.get_is_background_mining_enabled(); if ( lMiner.is_mining() ) { res.speed = lMiner.get_speed(); @@ -736,7 +776,7 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res) { - mlog_set_categories(req.categories.c_str()); + mlog_set_log(req.categories.c_str()); res.status = CORE_RPC_STATUS_OK; return true; } @@ -749,6 +789,14 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res) + { + CHECK_CORE_BUSY(); + m_core.get_pool_transaction_hashes(res.tx_hashes); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) { // FIXME: replace back to original m_p2p.send_stop_signal() after @@ -835,7 +883,7 @@ namespace cryptonote block b = AUTO_VAL_INIT(b); cryptonote::blobdata blob_reserve; blob_reserve.resize(req.reserve_size, 0); - if(!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) + if(!m_core.get_block_template(b, acc, res.difficulty, res.height, res.expected_reward, blob_reserve)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Internal error: failed to create block template"; @@ -1386,6 +1434,7 @@ namespace cryptonote std::pair<uint64_t, uint64_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count); res.emission_amount = amounts.first; res.fee_amount = amounts.second; + res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1436,21 +1485,155 @@ namespace cryptonote bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res) { m_p2p.set_save_graph(true); + res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res) { m_p2p.set_save_graph(false); + res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res) + { + static const char software[] = "monero"; +#ifdef BUILD_TAG + static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); +#else + static const char buildtag[] = "source"; +#endif - const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_bind_ip = { - "rpc-bind-ip" - , "IP for RPC server" - , "127.0.0.1" - }; + if (req.command != "check" && req.command != "download" && req.command != "update") + { + res.status = std::string("unknown command: '") + req.command + "'"; + return true; + } + + std::string version, hash; + if (!tools::check_updates(software, buildtag, version, hash)) + { + res.status = "Error checking for updates"; + return true; + } + if (tools::vercmp(version.c_str(), MONERO_VERSION) <= 0) + { + res.update = false; + res.status = CORE_RPC_STATUS_OK; + return true; + } + res.update = true; + res.version = version; + res.user_uri = tools::get_update_url(software, "cli", buildtag, version, true); + res.auto_uri = tools::get_update_url(software, "cli", buildtag, version, false); + res.hash = hash; + if (req.command == "check") + { + res.status = CORE_RPC_STATUS_OK; + return true; + } + + boost::filesystem::path path; + if (req.path.empty()) + { + std::string filename; + const char *slash = strrchr(res.auto_uri.c_str(), '/'); + if (slash) + filename = slash + 1; + else + filename = std::string(software) + "-update-" + version; + path = epee::string_tools::get_current_module_folder(); + path /= filename; + } + else + { + path = req.path; + } + + crypto::hash file_hash; + if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash))) + { + MDEBUG("We don't have that file already, downloading"); + if (!tools::download(path.string(), res.auto_uri)) + { + MERROR("Failed to download " << res.auto_uri); + return false; + } + if (!tools::sha256sum(path.string(), file_hash)) + { + MERROR("Failed to hash " << path); + return false; + } + if (hash != epee::string_tools::pod_to_hex(file_hash)) + { + MERROR("Download from " << res.auto_uri << " does not match the expected hash"); + return false; + } + MINFO("New version downloaded to " << path); + } + else + { + MDEBUG("We already have " << path << " with expected hash"); + } + res.path = path.string(); + + if (req.command == "download") + { + res.status = CORE_RPC_STATUS_OK; + return true; + } + + res.status = "'update' not implemented yet"; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp) + { + if(!check_core_busy()) + { + error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; + error_resp.message = "Core is busy."; + return false; + } + + bool failed = false; + for (const auto &str: req.txids) + { + cryptonote::blobdata txid_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(str, txid_data)) + { + res.status = std::string("Invalid transaction id: ") + str; + failed = true; + } + crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); + + cryptonote::transaction tx; + bool r = m_core.get_pool_transaction(txid, tx); + if (r) + { + cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); + NOTIFY_NEW_TRANSACTIONS::request r; + r.txs.push_back(cryptonote::tx_to_blob(tx)); + m_core.get_protocol()->relay_transactions(r, fake_context); + //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes + } + else + { + res.status = std::string("Transaction not found in pool: ") + str; + failed = true; + } + } + + if (failed) + { + return false; + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_bind_port = { "rpc-bind-port" @@ -1469,11 +1652,4 @@ namespace cryptonote , "Restrict RPC to view only commands" , false }; - - const command_line::arg_descriptor<std::string> core_rpc_server::arg_user_agent = { - "user-agent" - , "Restrict RPC to clients using this user agent" - , "" - }; - } // namespace cryptonote diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 767bcc715..0aa222c87 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -52,11 +52,9 @@ namespace cryptonote { public: - static const command_line::arg_descriptor<std::string> arg_rpc_bind_ip; static const command_line::arg_descriptor<std::string> arg_rpc_bind_port; static const command_line::arg_descriptor<std::string> arg_testnet_rpc_bind_port; static const command_line::arg_descriptor<bool> arg_restricted_rpc; - static const command_line::arg_descriptor<std::string> arg_user_agent; typedef epee::net_utils::connection_context_base connection_context; @@ -94,12 +92,14 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted) MAP_URI_AUTO_JON2_IF("/set_log_categories", on_set_log_categories, COMMAND_RPC_SET_LOG_CATEGORIES, !m_restricted) MAP_URI_AUTO_JON2("/get_transaction_pool", on_get_transaction_pool, COMMAND_RPC_GET_TRANSACTION_POOL) + MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES) MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted) MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted) MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) + MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted) BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH) @@ -121,6 +121,7 @@ namespace cryptonote MAP_JON_RPC_WE("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM) MAP_JON_RPC_WE("get_fee_estimate", on_get_per_kb_fee_estimate, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE) MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted) + MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted) END_JSON_RPC_MAP() END_URI_MAP2() @@ -146,10 +147,12 @@ namespace cryptonote bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res); bool on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res); bool on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res); + bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res); bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res); bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res); bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res); bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res); + bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res); //json_rpc bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); @@ -172,13 +175,10 @@ namespace cryptonote bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp); bool on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp); bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp); + bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp); //----------------------- private: - - bool handle_command_line( - const boost::program_options::variables_map& vm - ); bool check_core_busy(); bool check_core_ready(); @@ -188,8 +188,6 @@ private: core& m_core; nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& m_p2p; - std::string m_port; - std::string m_bind_ip; bool m_testnet; bool m_restricted; }; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 0fc230d11..9bdadf0d1 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,8 +30,8 @@ #pragma once #include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/difficulty.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/difficulty.h" #include "crypto/hash.h" namespace cryptonote @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 6 +#define CORE_RPC_VERSION_MINOR 10 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -80,9 +80,11 @@ namespace cryptonote { std::list<crypto::hash> block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ uint64_t start_height; + bool prune; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) KV_SERIALIZE(start_height) + KV_SERIALIZE(prune) END_KV_SERIALIZE_MAP() }; @@ -500,10 +502,14 @@ namespace cryptonote { std::string miner_address; uint64_t threads_count; + bool do_background_mining; + bool ignore_battery; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(miner_address) KV_SERIALIZE(threads_count) + KV_SERIALIZE(do_background_mining) + KV_SERIALIZE(ignore_battery) END_KV_SERIALIZE_MAP() }; @@ -608,6 +614,7 @@ namespace cryptonote uint64_t speed; uint32_t threads_count; std::string address; + bool is_background_mining_enabled; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -615,6 +622,7 @@ namespace cryptonote KV_SERIALIZE(speed) KV_SERIALIZE(threads_count) KV_SERIALIZE(address) + KV_SERIALIZE(is_background_mining_enabled) END_KV_SERIALIZE_MAP() }; }; @@ -684,6 +692,7 @@ namespace cryptonote uint64_t difficulty; uint64_t height; uint64_t reserved_offset; + uint64_t expected_reward; std::string prev_hash; blobdata blocktemplate_blob; blobdata blockhashing_blob; @@ -693,6 +702,7 @@ namespace cryptonote KV_SERIALIZE(difficulty) KV_SERIALIZE(height) KV_SERIALIZE(reserved_offset) + KV_SERIALIZE(expected_reward) KV_SERIALIZE(prev_hash) KV_SERIALIZE(blocktemplate_blob) KV_SERIALIZE(blockhashing_blob) @@ -1017,6 +1027,26 @@ namespace cryptonote }; }; + struct COMMAND_RPC_GET_TRANSACTION_POOL_HASHES + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::vector<crypto::hash> tx_hashes; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(tx_hashes) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_GET_CONNECTIONS { struct request @@ -1432,4 +1462,60 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct COMMAND_RPC_UPDATE + { + struct request + { + std::string command; + std::string path; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(command); + KV_SERIALIZE(path); + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + bool update; + std::string version; + std::string user_uri; + std::string auto_uri; + std::string hash; + std::string path; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(update) + KV_SERIALIZE(version) + KV_SERIALIZE(user_uri) + KV_SERIALIZE(auto_uri) + KV_SERIALIZE(hash) + KV_SERIALIZE(path) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_RELAY_TX + { + struct request + { + std::list<std::string> txids; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txids) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; } diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 4ac48a1c1..269cce2b1 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp new file mode 100644 index 000000000..4435f74d1 --- /dev/null +++ b/src/rpc/rpc_args.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#include "rpc_args.h" + +#include <boost/asio/ip/address.hpp> +#include "common/command_line.h" +#include "common/i18n.h" + +namespace cryptonote +{ + rpc_args::descriptors::descriptors() + : rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify ip to bind rpc server"), "127.0.0.1"}) + , rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true}) + , confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")}) + {} + + const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); } + + void rpc_args::init_options(boost::program_options::options_description& desc) + { + const descriptors arg{}; + command_line::add_arg(desc, arg.rpc_bind_ip); + command_line::add_arg(desc, arg.rpc_login); + command_line::add_arg(desc, arg.confirm_external_bind); + } + + boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm) + { + const descriptors arg{}; + rpc_args config{}; + + config.bind_ip = command_line::get_arg(vm, arg.rpc_bind_ip); + if (!config.bind_ip.empty()) + { + // always parse IP here for error consistency + boost::system::error_code ec{}; + const auto parsed_ip = boost::asio::ip::address::from_string(config.bind_ip, ec); + if (ec) + { + LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_bind_ip.name); + return boost::none; + } + + if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg.confirm_external_bind)) + { + LOG_ERROR( + "--" << arg.rpc_bind_ip.name << + tr(" permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --") << + arg.confirm_external_bind.name + ); + return boost::none; + } + } + + if (command_line::has_arg(vm, arg.rpc_login)) + { + config.login = tools::login::parse(command_line::get_arg(vm, arg.rpc_login), true, "RPC server password"); + if (!config.login) + return boost::none; + + if (config.login->username.empty()) + { + LOG_ERROR(tr("Username specified with --") << arg.rpc_login.name << tr(" cannot be empty")); + return boost::none; + } + } + + return {std::move(config)}; + } +} diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h new file mode 100644 index 000000000..d6e7bab07 --- /dev/null +++ b/src/rpc/rpc_args.h @@ -0,0 +1,67 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +#pragma once + +#include <boost/optional/optional.hpp> +#include <boost/program_options/options_description.hpp> +#include <boost/program_options/variables_map.hpp> +#include <string> + +#include "common/command_line.h" +#include "common/password.h" + +namespace cryptonote +{ + //! Processes command line arguments related to server-side RPC + struct rpc_args + { + // non-static construction prevents initialization order issues + struct descriptors + { + descriptors(); + descriptors(const descriptors&) = delete; + descriptors(descriptors&&) = delete; + descriptors& operator=(const descriptors&) = delete; + descriptors& operator=(descriptors&&) = delete; + + const command_line::arg_descriptor<std::string> rpc_bind_ip; + const command_line::arg_descriptor<std::string> rpc_login; + const command_line::arg_descriptor<bool> confirm_external_bind; + }; + + static const char* tr(const char* str); + static void init_options(boost::program_options::options_description& desc); + + //! \return Arguments specified by user, or `boost::none` if error + static boost::optional<rpc_args> process(const boost::program_options::variables_map& vm); + + std::string bind_ip; + boost::optional<tools::login> login; // currently `boost::none` if unspecified by user + }; +} diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index 3a5343c9c..0a267b081 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index ab4a86c68..08eba41da 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 9a7e89c49..4213f2e58 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h index f46a7ee2b..c5365aab7 100644 --- a/src/serialization/debug_archive.h +++ b/src/serialization/debug_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index 629a37311..8f74e26b1 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h index 5b7871072..32e7b69cf 100644 --- a/src/serialization/json_utils.h +++ b/src/serialization/json_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index dac43720b..639240820 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/string.h b/src/serialization/string.h index 2a5228a3f..b94f43dd8 100644 --- a/src/serialization/string.h +++ b/src/serialization/string.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/variant.h b/src/serialization/variant.h index a2cce9fa1..9048e2963 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/serialization/vector.h b/src/serialization/vector.h index 7f2bc78ba..598cfb92e 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index 259008ac5..18a8bac68 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a8f1d177f..6c2df4b22 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -50,7 +50,7 @@ #include "p2p/net_node.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "simplewallet.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "storages/http_abstract_invoke.h" #include "rpc/core_rpc_server_commands_defs.h" #include "crypto/crypto.h" // for crypto::secret_key definition @@ -101,6 +101,7 @@ enum TransferType { namespace { + const auto allowed_priority_strings = {"default", "unimportant", "normal", "elevated", "priority"}; const auto arg_wallet_file = wallet_args::arg_wallet_file(); const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""}; const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""}; @@ -377,12 +378,12 @@ bool simple_wallet::change_password(const std::vector<std::string> &args) if(orig_pwd_container == boost::none) { fail_msg_writer() << tr("Your original password was incorrect."); - return false; + return true; } - // prompts for a new password, this is not a new wallet so pass in false. - const auto pwd_container = tools::wallet2::password_prompt(false); - + // prompts for a new password, pass true to verify the password + const auto pwd_container = tools::wallet2::password_prompt(true); + try { m_wallet->rewrite(m_wallet_file, pwd_container->password()); @@ -391,7 +392,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args) catch (const tools::error::wallet_logic_error& e) { fail_msg_writer() << tr("Error with wallet rewrite: ") << e.what(); - return false; + return true; } return true; @@ -482,16 +483,11 @@ bool simple_wallet::set_default_mixin(const std::vector<std::string> &args/* = s bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { int priority = 0; - if (m_wallet->watch_only()) - { - fail_msg_writer() << tr("wallet is watch-only and cannot transfer"); - return true; - } try { if (strchr(args[1].c_str(), '-')) { - fail_msg_writer() << tr("priority must be 0, 1, 2, or 3 "); + fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4 "); return true; } if (args[1] == "0") @@ -501,9 +497,9 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* else { priority = boost::lexical_cast<int>(args[1]); - if (priority != 1 && priority != 2 && priority != 3) + if (priority < 1 || priority > 4) { - fail_msg_writer() << tr("priority must be 0, 1, 2, or 3"); + fail_msg_writer() << tr("priority must be 0, 1, 2, 3,or 4"); return true; } } @@ -518,7 +514,7 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* } catch(const boost::bad_lexical_cast &) { - fail_msg_writer() << tr("priority must be 0, 1, 2 or 3"); + fail_msg_writer() << tr("priority must be 0, 1, 2 3,or 4"); return true; } catch(...) @@ -564,27 +560,98 @@ bool simple_wallet::set_refresh_type(const std::vector<std::string> &args/* = st bool simple_wallet::set_confirm_missing_payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { - if (m_wallet->watch_only()) + const auto pwd_container = get_and_verify_password(); + if (pwd_container) { - fail_msg_writer() << tr("wallet is watch-only and cannot transfer"); + m_wallet->confirm_missing_payment_id(is_it_true(args[1])); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + return true; +} + +bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + m_wallet->ask_password(is_it_true(args[1])); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + return true; +} + +bool simple_wallet::set_unit(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const std::string &unit = args[1]; + unsigned int decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT; + + if (unit == "monero") + decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT; + else if (unit == "millinero") + decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 3; + else if (unit == "micronero") + decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 6; + else if (unit == "nanonero") + decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 9; + else if (unit == "piconero") + decimal_point = 0; + else + { + fail_msg_writer() << tr("invalid unit"); return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->confirm_missing_payment_id(is_it_true(args[1])); + m_wallet->set_default_decimal_point(decimal_point); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } -bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +bool simple_wallet::set_min_output_count(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { + uint32_t count; + if (!string_tools::get_xtype_from_string(count, args[1])) + { + fail_msg_writer() << tr("invalid count: must be an unsigned integer"); + return true; + } + const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->ask_password(is_it_true(args[1])); + m_wallet->set_min_output_count(count); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + return true; +} + +bool simple_wallet::set_min_output_value(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + uint64_t value; + if (!cryptonote::parse_amount(value, args[1])) + { + fail_msg_writer() << tr("invalid value"); + return true; + } + + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + m_wallet->set_min_output_value(value); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + } + return true; +} + +bool simple_wallet::set_merge_destinations(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + m_wallet->merge_destinations(is_it_true(args[1])); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; @@ -612,11 +679,12 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), tr("incoming_transfers [available|unavailable] - Show incoming transfers, all or filtered by availability")); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), tr("payments <PID_1> [<PID_2> ... <PID_N>] - Show payments for given payment ID[s]")); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height")); - m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [<mixin_count>] <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of extra inputs to include for untraceability (from 2 to maximum available)")); - m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm")); + m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("Same as transfer, but using an older transaction building algorithm")); + m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <mixin_count> is the number of extra inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [<mixin_count>] <addr> <amount> <lockblocks>(Number of blocks to lock the transaction for, max 1000000) [<payment_id>]")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0")); - m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [mixin] address [payment_id] - Send all unlocked balance an address")); + m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [mixin] address [payment_id] - Send all unlocked balance to an address")); + m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below <amount_threshold> [mixin] address [payment_id] - Send all unlocked outputs below the threshold to an address")); m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), tr("donate [<mixin_count>] <amount> [payment_id] - Donate <amount> to the development team (donate.getmonero.org)")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), tr("Sign a transaction from a file")); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file")); @@ -629,7 +697,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("viewkey", boost::bind(&simple_wallet::viewkey, this, _1), tr("Display private view key")); m_cmd_binder.set_handler("spendkey", boost::bind(&simple_wallet::spendkey, this, _1), tr("Display private spend key")); m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Display Electrum-style mnemonic seed")); - m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-mixin <n> - set default mixin (default is 4); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [1|2|3] - normal/elevated/priority fee; confirm-missing-payment-id <1|0>")); + m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-mixin <n> - set default mixin (default is 4); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [0|1|2|3|4] - default/unimportant/normal/elevated/priority fee; confirm-missing-payment-id <1|0>; ask-password <1|0>; unit <monero|millinero|micronero|nanonero|piconero> - set default monero (sub-)unit; min-output-count [n] - try to keep at least that many outputs of value at least min-output-value; min-output-value [n] - try to keep at least min-output-count outputs of at least that value - merge-destinations <1|0> - whether to merge multiple payments to the same destination address")); m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs")); m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given <txid>")); m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to <address> in <txid>")); @@ -646,6 +714,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("export_outputs", boost::bind(&simple_wallet::export_outputs, this, _1), tr("Export a set of outputs owned by this wallet")); m_cmd_binder.set_handler("import_outputs", boost::bind(&simple_wallet::import_outputs, this, _1), tr("Import set of outputs owned by this wallet")); m_cmd_binder.set_handler("show_transfer", boost::bind(&simple_wallet::show_transfer, this, _1), tr("Show information about a transfer to/from this address")); + m_cmd_binder.set_handler("password", boost::bind(&simple_wallet::change_password, this, _1), tr("Change wallet password")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help")); } //---------------------------------------------------------------------------------------------------- @@ -663,10 +732,29 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) success_msg_writer() << "priority = " << m_wallet->get_default_priority(); success_msg_writer() << "confirm-missing-payment-id = " << m_wallet->confirm_missing_payment_id(); success_msg_writer() << "ask-password = " << m_wallet->ask_password(); + success_msg_writer() << "unit = " << cryptonote::get_unit(m_wallet->get_default_decimal_point()); + success_msg_writer() << "min-outputs-count = " << m_wallet->get_min_output_count(); + success_msg_writer() << "min-outputs-value = " << cryptonote::print_money(m_wallet->get_min_output_value()); + success_msg_writer() << "merge-destinations = " << m_wallet->merge_destinations(); return true; } else { + +#define CHECK_SIMPLE_VARIABLE(name, f, help) do \ + if (args[0] == name) { \ + if (args.size() <= 1) \ + { \ + fail_msg_writer() << "set " << #name << ": " << tr("needs an argument") << " (" << help << ")"; \ + return true; \ + } \ + else \ + { \ + f(args); \ + return true; \ + } \ + } while(0) + if (args[0] == "seed") { if (args.size() == 1) @@ -680,124 +768,19 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) return true; } } - else if (args[0] == "always-confirm-transfers") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set always-confirm-transfers: needs an argument (0 or 1)"); - return true; - } - else - { - set_always_confirm_transfers(args); - return true; - } - } - else if (args[0] == "print-ring-members") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set print-ring-members: needs an argument (0 or 1)"); - return true; - } - else - { - set_print_ring_members(args); - return true; - } - } - else if (args[0] == "store-tx-info") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set store-tx-info: needs an argument (0 or 1)"); - return true; - } - else - { - set_store_tx_info(args); - return true; - } - } - else if (args[0] == "default-mixin") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set default-mixin: needs an argument (integer >= 2)"); - return true; - } - else - { - set_default_mixin(args); - return true; - } - } - else if (args[0] == "auto-refresh") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set auto-refresh: needs an argument (0 or 1)"); - return true; - } - else - { - set_auto_refresh(args); - return true; - } - } - else if (args[0] == "refresh-type") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set refresh-type: needs an argument:") << - tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)"); - return true; - } - else - { - set_refresh_type(args); - return true; - } - } - else if (args[0] == "priority") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set priority: needs an argument: 0, 1, 2, or 3"); - return true; - } - else - { - set_default_priority(args); - return true; - } - } - else if (args[0] == "confirm-missing-payment-id") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set confirm-missing-payment-id: needs an argument (0 or 1)"); - return true; - } - else - { - set_confirm_missing_payment_id(args); - return true; - } - } - else if (args[0] == "ask-password") - { - if (args.size() <= 1) - { - fail_msg_writer() << tr("set ask-password: needs an argument (0 or 1)"); - return true; - } - else - { - set_ask_password(args); - return true; - } - } + CHECK_SIMPLE_VARIABLE("always-confirm-transfers", set_always_confirm_transfers, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("print-ring-members", set_print_ring_members, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("store-tx-info", set_store_tx_info, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("default-mixin", set_default_mixin, tr("integer >= 2")); + CHECK_SIMPLE_VARIABLE("auto-refresh", set_auto_refresh, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)")); + CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4")); + CHECK_SIMPLE_VARIABLE("confirm-missing-payment-id", set_confirm_missing_payment_id, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("ask-password", set_ask_password, tr("0 or 1")); + CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("monero, millinero, micronero, nanop, piconero")); + CHECK_SIMPLE_VARIABLE("min-outputs-count", set_min_output_count, tr("unsigned integer")); + CHECK_SIMPLE_VARIABLE("min-outputs-value", set_min_output_value, tr("amount")); + CHECK_SIMPLE_VARIABLE("merge-destinations", set_merge_destinations, tr("0 or 1")); } fail_msg_writer() << tr("set: unrecognized argument(s)"); return true; @@ -811,21 +794,7 @@ bool simple_wallet::set_log(const std::vector<std::string> &args) fail_msg_writer() << tr("usage: set_log <log_level_number_0-4> | <categories>"); return true; } - uint16_t l = 0; - if(epee::string_tools::get_xtype_from_string(l, args[0])) - { - if(4 < l) - { - fail_msg_writer() << tr("wrong number range, use: set_log <log_level_number_0-4>"); - return true; - } - - mlog_set_log_level(l); - } - else - { - mlog_set_categories(args.front().c_str()); - } + mlog_set_log(args[0].c_str()); return true; } //---------------------------------------------------------------------------------------------------- @@ -923,6 +892,15 @@ void simple_wallet::print_seed(std::string seed) std::cout << seed << std::endl; } //---------------------------------------------------------------------------------------------------- +static bool might_be_partial_seed(std::string words) +{ + std::vector<std::string> seed; + + boost::algorithm::trim(words); + boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on); + return seed.size() < 24; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::init(const boost::program_options::variables_map& vm) { if (!handle_command_line(vm)) @@ -952,14 +930,20 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (m_electrum_seed.empty()) { - m_electrum_seed = command_line::input_line("Specify Electrum seed: "); - if (std::cin.eof()) - return false; - if (m_electrum_seed.empty()) + m_electrum_seed = ""; + do { - fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\""); - return false; - } + const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: "; + std::string electrum_seed = command_line::input_line(prompt); + if (std::cin.eof()) + return false; + if (electrum_seed.empty()) + { + fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\""); + return false; + } + m_electrum_seed += electrum_seed + " "; + } while (might_be_partial_seed(m_electrum_seed)); } if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key, old_language)) @@ -997,7 +981,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) { fail_msg_writer() << tr("failed to parse view key secret key"); return false; @@ -1049,7 +1033,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } cryptonote::blobdata spendkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) { fail_msg_writer() << tr("failed to parse spend key secret key"); return false; @@ -1065,7 +1049,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) return false; } cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) { fail_msg_writer() << tr("failed to parse view key secret key"); return false; @@ -1192,7 +1176,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } catch (const std::exception &e) { } - m_http_client.set_server(m_wallet->get_daemon_address()); + m_http_client.set_server(m_wallet->get_daemon_address(), m_wallet->get_daemon_login()); m_wallet->callback(this); return true; } @@ -1282,7 +1266,7 @@ std::string simple_wallet::get_mnemonic_language() fail_msg_writer() << tr("invalid language choice passed. Please try again.\n"); } } - catch (std::exception &e) + catch (const std::exception &e) { fail_msg_writer() << tr("invalid language choice passed. Please try again.\n"); } @@ -1561,16 +1545,15 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args) return true; assert(m_wallet); - COMMAND_RPC_START_MINING::request req; + COMMAND_RPC_START_MINING::request req = AUTO_VAL_INIT(req); req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); bool ok = true; size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2)); - if (0 == args.size()) - { - req.threads_count = 1; - } - else if (1 == args.size()) + size_t arg_size = args.size(); + if(arg_size >= 3) req.ignore_battery = args[2] == "true"; + if(arg_size >= 2) req.do_background_mining = args[1] == "true"; + if(arg_size >= 1) { uint16_t num = 1; ok = string_tools::get_xtype_from_string(num, args[0]); @@ -1579,12 +1562,12 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args) } else { - ok = false; + req.threads_count = 1; } if (!ok) { - fail_msg_writer() << tr("invalid arguments. Please use start_mining [<number_of_threads>], " + fail_msg_writer() << tr("invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery], " "<number_of_threads> should be from 1 to ") << max_mining_threads_count; return true; } @@ -1639,11 +1622,11 @@ void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block m_refresh_progress_reporter.update(height, false); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) +void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) { message_writer(console_color_green, false) << "\r" << tr("Height ") << height << ", " << - tr("transaction ") << get_transaction_hash(tx) << ", " << + tr("transaction ") << txid << ", " << tr("received ") << print_money(amount); if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); @@ -1651,16 +1634,16 @@ void simple_wallet::on_money_received(uint64_t height, const cryptonote::transac m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) +void simple_wallet::on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) { // Not implemented in CLI wallet } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) +void simple_wallet::on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) { message_writer(console_color_magenta, false) << "\r" << tr("Height ") << height << ", " << - tr("transaction ") << get_transaction_hash(spend_tx) << ", " << + tr("transaction ") << txid << ", " << tr("spent ") << print_money(amount); if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); @@ -1668,11 +1651,11 @@ void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transactio m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) +void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) { message_writer(console_color_red, true) << "\r" << tr("Height ") << height << ", " << - tr("transaction ") << get_transaction_hash(tx) << ", " << + tr("transaction ") << txid << ", " << tr("unsupported transaction format"); if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); @@ -2091,6 +2074,18 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri std::vector<std::string> local_args = args_; + int priority = 0; + if(local_args.size() > 0) { + auto priority_pos = std::find( + allowed_priority_strings.begin(), + allowed_priority_strings.end(), + local_args[0]); + if(priority_pos != allowed_priority_strings.end()) { + local_args.erase(local_args.begin()); + priority = std::distance(allowed_priority_strings.begin(), priority_pos); + } + } + size_t fake_outs_count; if(local_args.size() > 0) { if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) @@ -2175,7 +2170,10 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri bool has_payment_id; crypto::hash8 new_payment_id; if (!cryptonote::get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[i])) + { + fail_msg_writer() << tr("failed to parse address"); return true; + } if (has_payment_id) { @@ -2217,8 +2215,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } } @@ -2239,18 +2235,24 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri return true; } unlock_block = bc_height + locked_blocks; - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_trusted_daemon); break; case TransferNew: - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon); break; default: LOG_ERROR("Unknown transfer method, using original"); case TransferOriginal: - ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon); break; } + if (ptx_vector.empty()) + { + fail_msg_writer() << tr("No outputs found, or daemon is not ready"); + return true; + } + // if more than one tx necessary, prompt user to confirm if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1) { @@ -2306,8 +2308,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } } @@ -2486,8 +2486,6 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } @@ -2603,7 +2601,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::sweep_all(const std::vector<std::string> &args_) +bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &args_) { if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } if (!try_connect_to_daemon()) @@ -2670,7 +2668,10 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) crypto::hash8 new_payment_id; cryptonote::account_public_address address; if (!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[0])) + { + fail_msg_writer() << tr("failed to parse address"); return true; + } if (has_payment_id) { @@ -2701,8 +2702,6 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } } @@ -2710,11 +2709,11 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_all(address, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + auto ptx_vector = m_wallet->create_transactions_all(below, address, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); if (ptx_vector.empty()) { - fail_msg_writer() << tr("No outputs found"); + fail_msg_writer() << tr("No outputs found, or daemon is not ready"); return true; } @@ -2748,8 +2747,6 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { fail_msg_writer() << tr("transaction cancelled."); - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* return true; } @@ -2865,9 +2862,29 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::sweep_all(const std::vector<std::string> &args_) +{ + return sweep_main(0, args_); +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::sweep_below(const std::vector<std::string> &args_) +{ + uint64_t below = 0; + if (args_.size() < 1) + { + fail_msg_writer() << tr("missing amount threshold"); + return true; + } + if (!cryptonote::parse_amount(below, args_[0])) + { + fail_msg_writer() << tr("invalid amount threshold"); + return true; + } + return sweep_main(below, std::vector<std::string>(++args_.begin(), args_.end())); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::donate(const std::vector<std::string> &args_) { - if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } std::vector<std::string> local_args = args_; if(local_args.empty() || local_args.size() > 3) { @@ -2915,6 +2932,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, size_t min_mixin = ~0; std::unordered_map<std::string, uint64_t> dests; const std::string wallet_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + int first_known_non_zero_change_index = -1; for (size_t n = 0; n < get_num_txes(); ++n) { const tools::wallet2::tx_construction_data &cd = get_tx(n); @@ -2949,10 +2967,15 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, fail_msg_writer() << tr("Claimed change is larger than payment to the change address"); return false; } - if (memcmp(&cd.change_dts.addr, &get_tx(0).change_dts.addr, sizeof(cd.change_dts.addr))) + if (cd.change_dts.amount > 0) { - fail_msg_writer() << tr("Change does to more than one address"); - return false; + if (first_known_non_zero_change_index == -1) + first_known_non_zero_change_index = n; + if (memcmp(&cd.change_dts.addr, &get_tx(first_known_non_zero_change_index).change_dts.addr, sizeof(cd.change_dts.addr))) + { + fail_msg_writer() << tr("Change goes to more than one address"); + return false; + } } change += cd.change_dts.amount; it->second -= cd.change_dts.amount; @@ -3160,10 +3183,10 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_) if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(local_args.front(), txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(local_args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -3197,7 +3220,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_) assert(m_wallet); cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[0], txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[0], txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); return true; @@ -3213,7 +3236,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_) } crypto::secret_key tx_key; cryptonote::blobdata tx_key_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[1], tx_key_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[1], tx_key_data) || tx_key_data.size() != sizeof(crypto::secret_key)) { fail_msg_writer() << tr("failed to parse tx key"); return true; @@ -3845,10 +3868,10 @@ bool simple_wallet::set_tx_note(const std::vector<std::string> &args) } cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -3873,10 +3896,10 @@ bool simple_wallet::get_tx_note(const std::vector<std::string> &args) } cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -4003,7 +4026,7 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args) return true; } } - catch (std::exception &e) + catch (const std::exception &e) { LOG_ERROR("Error exporting key images: " << e.what()); fail_msg_writer() << "Error exporting key images: " << e.what(); @@ -4173,10 +4196,10 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) } cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash)) { fail_msg_writer() << tr("failed to parse txid"); - return false; + return true; } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -4328,7 +4351,8 @@ int main(int argc, char* argv[]) argc, argv, "monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]", desc_params, - positional_options + positional_options, + "monero-wallet-cli.log" ); if (!vm) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index ce0a24be7..1b58ff32a 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -40,11 +40,11 @@ #include <boost/optional/optional.hpp> #include <boost/program_options/variables_map.hpp> -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "wallet/wallet2.h" #include "console_handler.h" -#include "wallet/password_container.h" +#include "common/password.h" #include "crypto/crypto.h" // for definition of crypto::secret_key #undef MONERO_DEFAULT_LOG_CATEGORY @@ -116,6 +116,10 @@ namespace cryptonote bool set_refresh_type(const std::vector<std::string> &args = std::vector<std::string>()); bool set_confirm_missing_payment_id(const std::vector<std::string> &args = std::vector<std::string>()); bool set_ask_password(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_unit(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_min_output_count(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_min_output_value(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_merge_destinations(const std::vector<std::string> &args = std::vector<std::string>()); bool help(const std::vector<std::string> &args = std::vector<std::string>()); bool start_mining(const std::vector<std::string> &args); bool stop_mining(const std::vector<std::string> &args); @@ -130,7 +134,9 @@ namespace cryptonote bool transfer(const std::vector<std::string> &args); bool transfer_new(const std::vector<std::string> &args); bool locked_transfer(const std::vector<std::string> &args); + bool sweep_main(uint64_t below, const std::vector<std::string> &args); bool sweep_all(const std::vector<std::string> &args); + bool sweep_below(const std::vector<std::string> &args); bool sweep_unmixable(const std::vector<std::string> &args); bool donate(const std::vector<std::string> &args); bool sign_transfer(const std::vector<std::string> &args); @@ -190,10 +196,10 @@ namespace cryptonote //----------------- i_wallet2_callback --------------------- virtual void on_new_block(uint64_t height, const cryptonote::block& block); - virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount); - virtual void on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount); - virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx); - virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx); + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount); + virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount); + virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx); + virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx); //---------------------------------------------------------- friend class refresh_progress_reporter_t; diff --git a/src/version.cmake b/src/version.cmake index d60673ae2..623d3cf1f 100644 --- a/src/version.cmake +++ b/src/version.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/src/version.h.in b/src/version.h.in index 43e046f24..852c8023b 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define MONERO_VERSION_TAG "@VERSIONTAG@" -#define MONERO_VERSION "0.10.1.0" +#define MONERO_VERSION "0.10.3.1" #define MONERO_RELEASE_NAME "Wolfram Warptangent" #define MONERO_VERSION_FULL MONERO_VERSION "-" MONERO_VERSION_TAG diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 922464a3c..2e7610b64 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -31,7 +31,6 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(wallet_sources - password_container.cpp wallet2.cpp wallet_args.cpp node_rpc_proxy.cpp @@ -49,7 +48,6 @@ set(wallet_api_headers set(wallet_private_headers - password_container.h wallet2.h wallet_args.h wallet_errors.h @@ -74,6 +72,7 @@ monero_add_library(wallet ${wallet_private_headers}) target_link_libraries(wallet PUBLIC + common cryptonote_core mnemonics p2p @@ -127,14 +126,19 @@ endif() # build and install libwallet_merged only if we building for GUI if (BUILD_GUI_DEPS) - set(libs_to_merge wallet cryptonote_core mnemonics common crypto ringct) + set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common crypto ringct) foreach(lib ${libs_to_merge}) list(APPEND objlibs $<TARGET_OBJECTS:obj_${lib}>) # matches naming convention in src/CMakeLists.txt endforeach() add_library(wallet_merged STATIC ${objlibs}) + if(IOS) + set(lib_folder lib-${ARCH}) + else() + set(lib_folder lib) + endif() install(TARGETS wallet_merged - ARCHIVE DESTINATION lib) + ARCHIVE DESTINATION ${lib_folder}) install(FILES ${wallet_api_headers} DESTINATION include/wallet) diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index e397dac04..28f835ebd 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -180,4 +180,4 @@ AddressBookImpl::~AddressBookImpl() } // namespace -namespace Bitmonero = Monero;
\ No newline at end of file +namespace Bitmonero = Monero; diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h index 5f72c5860..25f59128b 100644 --- a/src/wallet/api/address_book.h +++ b/src/wallet/api/address_book.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 760c84f4f..9798d66c6 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,9 +32,8 @@ #include "wallet.h" #include "common_defines.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include <memory> #include <vector> @@ -125,7 +124,7 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) m_errorString = writer.str(); if (!reason.empty()) m_errorString += string(tr(". Reason: ")) + reason; - } catch (std::exception &e) { + } catch (const std::exception &e) { m_errorString = string(tr("Unknown exception: ")) + e.what(); m_status = Status_Error; } catch (...) { diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h index d85a686fd..0c3e95a85 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index 1e50652c6..85f2b05ce 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h index 37c9ea0e4..4987bdab2 100644 --- a/src/wallet/api/transaction_history.h +++ b/src/wallet/api/transaction_history.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp index 576ae8532..79a8fe9b5 100644 --- a/src/wallet/api/transaction_info.cpp +++ b/src/wallet/api/transaction_info.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h index af9696daf..16fa5da7a 100644 --- a/src/wallet/api/transaction_info.h +++ b/src/wallet/api/transaction_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index 84ec2d9d2..1d9ef5d7c 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -32,9 +32,8 @@ #include "wallet.h" #include "common_defines.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include <memory> #include <vector> @@ -105,6 +104,7 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu size_t min_mixin = ~0; std::unordered_map<std::string, uint64_t> dests; const std::string wallet_address = m_wallet.m_wallet->get_account().get_public_address_str(m_wallet.m_wallet->testnet()); + int first_known_non_zero_change_index = -1; for (size_t n = 0; n < get_num_txes(); ++n) { const tools::wallet2::tx_construction_data &cd = get_tx(n); @@ -141,11 +141,16 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu m_errorString = tr("Claimed change is larger than payment to the change address"); return false; } - if (memcmp(&cd.change_dts.addr, &get_tx(0).change_dts.addr, sizeof(cd.change_dts.addr))) + if (cd.change_dts.amount > 0) { - m_status = Status_Error; - m_errorString = tr("Change does to more than one address"); - return false; + if (first_known_non_zero_change_index == -1) + first_known_non_zero_change_index = n; + if (memcmp(&cd.change_dts.addr, &get_tx(first_known_non_zero_change_index).change_dts.addr, sizeof(cd.change_dts.addr))) + { + m_status = Status_Error; + m_errorString = tr("Change goes to more than one address"); + return false; + } } change += cd.change_dts.amount; it->second -= cd.change_dts.amount; diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h index 8038334e4..9c442f503 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp index c369427b4..a9646e038 100644 --- a/src/wallet/api/utils.cpp +++ b/src/wallet/api/utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 830f98909..21760ac49 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -44,6 +44,9 @@ using namespace std; using namespace cryptonote; +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" + namespace Monero { namespace { @@ -96,10 +99,10 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) + virtual void on_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) { - std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(tx)); + std::string tx_hash = epee::string_tools::pod_to_hex(txid); LOG_PRINT_L3(__FUNCTION__ << ": money received. height: " << height << ", tx: " << tx_hash @@ -111,10 +114,10 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) + virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) { - std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(tx)); + std::string tx_hash = epee::string_tools::pod_to_hex(txid); LOG_PRINT_L3(__FUNCTION__ << ": unconfirmed money received. height: " << height << ", tx: " << tx_hash @@ -126,11 +129,11 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, + virtual void on_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount, const cryptonote::transaction& spend_tx) { // TODO; - std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(spend_tx)); + std::string tx_hash = epee::string_tools::pod_to_hex(txid); LOG_PRINT_L3(__FUNCTION__ << ": money spent. height: " << height << ", tx: " << tx_hash << ", amount: " << print_money(amount)); @@ -141,7 +144,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) + virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid) { // TODO; } @@ -255,6 +258,14 @@ uint64_t Wallet::maximumAllowedAmount() return std::numeric_limits<uint64_t>::max(); } +void Wallet::init(const char *argv0, const char *default_log_base_name) { + epee::string_tools::set_module_name_and_folder(argv0); + mlog_configure(mlog_get_default_log_path(default_log_base_name), true); +} + +void Wallet::debug(const std::string &str) { + MDEBUG(str); +} ///////////////////////// WalletImpl implementation //////////////////////// WalletImpl::WalletImpl(bool testnet) @@ -645,9 +656,11 @@ string WalletImpl::keysFilename() const return m_wallet->get_keys_file(); } -bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit) +bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username, const std::string &daemon_password) { clearStatus(); + if(daemon_username != "") + m_daemon_login.emplace(daemon_username, daemon_password); return doInit(daemon_address, upper_transaction_size_limit); } @@ -820,7 +833,7 @@ bool WalletImpl::exportKeyImages(const string &filename) return false; } } - catch (std::exception &e) + catch (const std::exception &e) { LOG_ERROR("Error exporting key images: " << e.what()); m_errorString = e.what(); @@ -939,7 +952,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const static_cast<uint32_t>(priority), extra, m_trustedDaemon); } else { - transaction->m_pending_tx = m_wallet->create_transactions_all(addr, fake_outs_count, 0 /* unlock_time */, + transaction->m_pending_tx = m_wallet->create_transactions_all(0, addr, fake_outs_count, 0 /* unlock_time */, static_cast<uint32_t>(priority), extra, m_trustedDaemon); } @@ -1151,7 +1164,7 @@ void WalletImpl::setDefaultMixin(uint32_t arg) bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) { cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) return false; const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -1162,7 +1175,7 @@ bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) std::string WalletImpl::getUserNote(const std::string &txid) const { cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) return ""; const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); @@ -1172,7 +1185,7 @@ std::string WalletImpl::getUserNote(const std::string &txid) const std::string WalletImpl::getTxKey(const std::string &txid) const { cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) { return ""; } @@ -1354,7 +1367,7 @@ bool WalletImpl::isNewWallet() const bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit) { - if (!m_wallet->init(daemon_address, upper_transaction_size_limit)) + if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit)) return false; // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) @@ -1400,6 +1413,18 @@ bool WalletImpl::rescanSpent() } return true; } + + +void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const +{ + m_wallet->get_hard_fork_info(version, earliest_height); +} + +bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const +{ + return m_wallet->use_fork_rules(version,early_blocks); +} + } // namespace namespace Bitmonero = Monero; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 3994afaa3..c376dd6c1 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -78,7 +78,7 @@ public: bool store(const std::string &path); std::string filename() const; std::string keysFilename() const; - bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0); + bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = ""); bool connectToDaemon(); ConnectionStatus connected() const; void setTrustedDaemon(bool arg); @@ -99,7 +99,8 @@ public: bool watchOnly() const; bool rescanSpent(); bool testnet() const {return m_wallet->testnet();} - + void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const; + bool useForkRules(uint8_t version, int64_t early_blocks) const; PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, @@ -170,6 +171,7 @@ private: std::atomic<bool> m_rebuildWalletCache; // cache connection status to avoid unnecessary RPC calls mutable std::atomic<bool> m_is_connected; + boost::optional<epee::net_utils::http::login> m_daemon_login{}; }; diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index c761cc6d2..b2f947972 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,11 +33,16 @@ #include "wallet.h" #include "common_defines.h" #include "common/dns_utils.h" +#include "common/util.h" +#include "common/updates.h" +#include "version.h" #include "net/http_client.h" #include <boost/filesystem.hpp> #include <boost/regex.hpp> +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" namespace epee { unsigned int g_test_dbg_lock_sleep = 0; @@ -48,7 +53,7 @@ namespace { bool connect_and_invoke(const std::string& address, const std::string& path, const Request& request, Response& response) { epee::net_utils::http::http_simple_client client{}; - return client.set_server(address) && epee::net_utils::invoke_http_json(path, request, response, client); + return client.set_server(address, boost::none) && epee::net_utils::invoke_http_json(path, request, response, client); } } @@ -182,7 +187,7 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std: { error = ""; cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid_text, txid_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(txid_text, txid_data) || txid_data.size() != sizeof(crypto::hash)) { error = tr("failed to parse txid"); return false; @@ -196,7 +201,7 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std: } crypto::secret_key tx_key; cryptonote::blobdata tx_key_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txkey_text, tx_key_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(txkey_text, tx_key_data) || tx_key_data.size() != sizeof(crypto::hash)) { error = tr("failed to parse tx key"); return false; @@ -372,26 +377,6 @@ double WalletManagerImpl::miningHashRate() const return mres.speed; } -void WalletManagerImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const -{ - epee::json_rpc::request<cryptonote::COMMAND_RPC_HARD_FORK_INFO::request> req_t = AUTO_VAL_INIT(req_t); - epee::json_rpc::response<cryptonote::COMMAND_RPC_HARD_FORK_INFO::response, std::string> resp_t = AUTO_VAL_INIT(resp_t); - - version = 0; - earliest_height = 0; - - epee::net_utils::http::http_simple_client http_client; - req_t.jsonrpc = "2.0"; - req_t.id = epee::serialization::storage_entry(0); - req_t.method = "hard_fork_info"; - req_t.params.version = 0; - bool r = connect_and_invoke(m_daemonAddress, "/json_rpc", req_t, resp_t); - if (!r || resp_t.result.status != CORE_RPC_STATUS_OK) - return; - version = resp_t.result.version; - earliest_height = resp_t.result.earliest_height; -} - uint64_t WalletManagerImpl::blockTarget() const { cryptonote::COMMAND_RPC_GET_INFO::request ireq; @@ -412,13 +397,15 @@ bool WalletManagerImpl::isMining() const return mres.active; } -bool WalletManagerImpl::startMining(const std::string &address, uint32_t threads) +bool WalletManagerImpl::startMining(const std::string &address, uint32_t threads, bool background_mining, bool ignore_battery) { cryptonote::COMMAND_RPC_START_MINING::request mreq; cryptonote::COMMAND_RPC_START_MINING::response mres; mreq.miner_address = address; mreq.threads_count = threads; + mreq.ignore_battery = ignore_battery; + mreq.do_background_mining = background_mining; if (!connect_and_invoke(m_daemonAddress, "/start_mining", mreq, mres)) return false; @@ -443,6 +430,29 @@ std::string WalletManagerImpl::resolveOpenAlias(const std::string &address, bool return addresses.front(); } +std::tuple<bool, std::string, std::string, std::string, std::string> WalletManager::checkUpdates(const std::string &software, const std::string &subdir) +{ +#ifdef BUILD_TAG + static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); +#else + static const char buildtag[] = "source"; +#endif + + std::string version, hash; + MDEBUG("Checking for a new " << software << " version for " << buildtag); + if (!tools::check_updates(software, buildtag, version, hash)) + return std::make_tuple(false, "", "", "", ""); + + if (tools::vercmp(version.c_str(), MONERO_VERSION) > 0) + { + std::string user_url = tools::get_update_url(software, subdir, buildtag, version, true); + std::string auto_url = tools::get_update_url(software, subdir, buildtag, version, false); + MGINFO("Version " << version << " of " << software << " for " << buildtag << " is available: " << user_url << ", SHA256 hash " << hash); + return std::make_tuple(true, version, hash, user_url, auto_url); + } + return std::make_tuple(false, "", "", "", ""); +} + ///////////////////// WalletManagerFactory implementation ////////////////////// WalletManager *WalletManagerFactory::getWalletManager() @@ -451,7 +461,6 @@ WalletManager *WalletManagerFactory::getWalletManager() static WalletManagerImpl * g_walletManager = nullptr; if (!g_walletManager) { - mlog_configure("monero-wallet-gui.log", false); g_walletManager = new WalletManagerImpl(); } @@ -465,7 +474,7 @@ void WalletManagerFactory::setLogLevel(int level) void WalletManagerFactory::setLogCategories(const std::string &categories) { - mlog_set_categories(categories.c_str()); + mlog_set_log(categories.c_str()); } diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index ce9b70e96..033e8108f 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -59,10 +59,9 @@ public: uint64_t blockchainTargetHeight() const; uint64_t networkDifficulty() const; double miningHashRate() const; - void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const; uint64_t blockTarget() const; bool isMining() const; - bool startMining(const std::string &address, uint32_t threads = 1); + bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true); bool stopMining(); std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const; diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index cc249b5cc..03e1bbd98 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -36,6 +36,8 @@ using namespace epee; namespace tools { +static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); + NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex) : m_http_client(http_client) , m_daemon_rpc_mutex(mutex) @@ -45,8 +47,43 @@ NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_clien , m_dynamic_per_kb_fee_estimate(0) , m_dynamic_per_kb_fee_estimate_cached_height(0) , m_dynamic_per_kb_fee_estimate_grace_blocks(0) + , m_rpc_version(0) {} +void NodeRPCProxy::invalidate() +{ + m_height = 0; + m_height_time = 0; + for (size_t n = 0; n < 256; ++n) + m_earliest_height[n] = 0; + m_dynamic_per_kb_fee_estimate = 0; + m_dynamic_per_kb_fee_estimate_cached_height = 0; + m_dynamic_per_kb_fee_estimate_grace_blocks = 0; + m_rpc_version = 0; +} + +boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) +{ + const time_t now = time(NULL); + if (m_rpc_version == 0) + { + epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_VERSION::request> req_t = AUTO_VAL_INIT(req_t); + epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_VERSION::response, std::string> resp_t = AUTO_VAL_INIT(resp_t); + req_t.jsonrpc = "2.0"; + req_t.id = epee::serialization::storage_entry(0); + req_t.method = "get_version"; + m_daemon_rpc_mutex.lock(); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, resp_t.result.status, "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(resp_t.result.status == CORE_RPC_STATUS_OK, resp_t.result.status, "Failed to get daemon RPC version"); + m_rpc_version = resp_t.result.version; + } + rpc_version = m_rpc_version; + return boost::optional<std::string>(); +} + boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height) { const time_t now = time(NULL); @@ -56,7 +93,7 @@ boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height) cryptonote::COMMAND_RPC_GET_HEIGHT::response res = AUTO_VAL_INIT(res); m_daemon_rpc_mutex.lock(); - bool r = net_utils::invoke_http_json("/getheight", req, res, m_http_client); + bool r = net_utils::invoke_http_json("/getheight", req, res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, res.status, "Failed to connect to daemon"); @@ -85,7 +122,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, req_t.id = epee::serialization::storage_entry(0); req_t.method = "hard_fork_info"; req_t.params.version = version; - bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, resp_t.result.status, "Failed to connect to daemon"); @@ -115,7 +152,7 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6 req_t.id = epee::serialization::storage_entry(0); req_t.method = "get_fee_estimate"; req_t.params.grace_blocks = grace_blocks; - bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, resp_t.result.status, "Failed to connect to daemon"); diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index e2f42d541..02d1d8d93 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -41,6 +41,9 @@ class NodeRPCProxy public: NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex); + void invalidate(); + + boost::optional<std::string> get_rpc_version(uint32_t &version); boost::optional<std::string> get_height(uint64_t &height); void set_height(uint64_t h); boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height); @@ -56,6 +59,7 @@ private: uint64_t m_dynamic_per_kb_fee_estimate; uint64_t m_dynamic_per_kb_fee_estimate_cached_height; uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks; + uint32_t m_rpc_version; }; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c52281a2b..9069789ca 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -39,10 +39,10 @@ using namespace epee; #include "cryptonote_config.h" #include "wallet2.h" #include "wallet2_api.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "rpc/core_rpc_server_commands_defs.h" #include "misc_language.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "common/boost_serialization_helper.h" #include "common/command_line.h" #include "profile_tools.h" @@ -82,8 +82,8 @@ using namespace cryptonote; #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003" #define SIGNED_TX_PREFIX "Monero signed tx set\003" -#define RECENT_OUTPUT_RATIO (0.25) // 25% of outputs are from the recent zone -#define RECENT_OUTPUT_ZONE (5 * 86400) // last 5 days are the recent zone +#define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone +#define RECENT_OUTPUT_ZONE ((time_t)(1.8 * 86400)) // last 1.8 day makes up the recent zone (taken from monerolink.pdf, Miller et al) #define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks @@ -105,9 +105,10 @@ namespace struct options { const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""}; const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""}; - const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password"), "", true}; + const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true}; const command_line::arg_descriptor<std::string> password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true}; const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 18081"), 0}; + const command_line::arg_descriptor<std::string> daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true}; const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false}; const command_line::arg_descriptor<bool> restricted = {"restricted-rpc", tools::wallet2::tr("Restricts to view-only commands"), false}; }; @@ -152,6 +153,18 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl return nullptr; } + boost::optional<epee::net_utils::http::login> login{}; + if (command_line::has_arg(vm, opts.daemon_login)) + { + auto parsed = tools::login::parse( + command_line::get_arg(vm, opts.daemon_login), false, "Daemon client password" + ); + if (!parsed) + return nullptr; + + login.emplace(std::move(parsed->username), std::move(parsed->password).password()); + } + if (daemon_host.empty()) daemon_host = "localhost"; @@ -164,7 +177,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(testnet, restricted)); - wallet->init(std::move(daemon_address)); + wallet->init(std::move(daemon_address), std::move(login)); return wallet; } @@ -200,8 +213,10 @@ boost::optional<tools::password_container> get_password(const boost::program_opt return tools::wallet2::password_prompt(verify); } -std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, bool testnet, bool restricted) +std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts) { + const bool testnet = command_line::get_arg(vm, opts.testnet); + /* GET_FIELD_FROM_JSON_RETURN_ON_ERROR Is a generic macro that can return false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly fails. This large wrapper is for the use of that macro */ @@ -238,7 +253,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, if (field_viewkey_found) { cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) { tools::fail_msg_writer() << tools::wallet2::tr("failed to parse view key secret key"); return false; @@ -256,7 +271,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, if (field_spendkey_found) { cryptonote::blobdata spendkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data)) + if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) { tools::fail_msg_writer() << tools::wallet2::tr("failed to parse spend key secret key"); return false; @@ -342,7 +357,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, return false; } - wallet.reset(new tools::wallet2(testnet, restricted)); + wallet.reset(make_basic(vm, opts).release()); wallet->set_refresh_from_block_height(field_scan_from_height); try @@ -434,14 +449,15 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.password); command_line::add_arg(desc_params, opts.password_file); command_line::add_arg(desc_params, opts.daemon_port); + command_line::add_arg(desc_params, opts.daemon_login); command_line::add_arg(desc_params, opts.testnet); command_line::add_arg(desc_params, opts.restricted); } -boost::optional<password_container> wallet2::password_prompt(const bool is_new_wallet) +boost::optional<password_container> wallet2::password_prompt(const bool new_password) { auto pwd_container = tools::password_container::prompt( - is_new_wallet, (is_new_wallet ? tr("Enter a password for your new wallet") : tr("Wallet password")) + new_password, (new_password ? tr("Enter new wallet password") : tr("Wallet password")) ); if (!pwd_container) { @@ -453,7 +469,7 @@ boost::optional<password_container> wallet2::password_prompt(const bool is_new_w std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file) { const options opts{}; - return generate_from_json(json_file, command_line::get_arg(vm, opts.testnet), command_line::get_arg(vm, opts.restricted)); + return generate_from_json(json_file, vm, opts); } std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file( @@ -484,14 +500,21 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const return {make_basic(vm, opts), std::move(*pwd)}; } +std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm) +{ + const options opts{}; + return make_basic(vm, opts); +} + //---------------------------------------------------------------------------------------------------- -bool wallet2::init(std::string daemon_address, uint64_t upper_transaction_size_limit) +bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit) { if(m_http_client.is_connected()) m_http_client.disconnect(); m_upper_transaction_size_limit = upper_transaction_size_limit; m_daemon_address = std::move(daemon_address); - return m_http_client.set_server(get_daemon_address()); + m_daemon_login = std::move(daemon_login); + return m_http_client.set_server(get_daemon_address(), get_daemon_login()); } //---------------------------------------------------------------------------------------------------- bool wallet2::is_deterministic() const @@ -618,28 +641,13 @@ bool wallet2::wallet_generate_key_image_helper(const cryptonote::account_keys& a return true; } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool) +void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool) { - class lazy_txid_getter - { - const cryptonote::transaction &tx; - crypto::hash lazy_txid; - bool computed; - public: - lazy_txid_getter(const transaction &tx): tx(tx), computed(false) {} - const crypto::hash &operator()() - { - if (!computed) - { - lazy_txid = cryptonote::get_transaction_hash(tx); - computed = true; - } - return lazy_txid; - } - } txid(tx); + // In this function, tx (probably) only contains the base information + // (that is, the prunable stuff may or may not be included) if (!miner_tx) - process_unconfirmed(tx, height); + process_unconfirmed(txid, tx, height); std::vector<size_t> outs; uint64_t tx_money_got_in_outs = 0; crypto::public_key tx_pub_key = null_pkey; @@ -648,7 +656,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s if(!parse_tx_extra(tx.extra, tx_extra_fields)) { // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key - LOG_PRINT_L0("Transaction extra has unsupported format: " << txid()); + LOG_PRINT_L0("Transaction extra has unsupported format: " << txid); } // Don't try to extract tx public key if tx has no ouputs @@ -662,9 +670,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s { if (pk_index > 1) break; - LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid()); + LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid); if(0 != m_callback) - m_callback->on_skip_transaction(height, tx); + m_callback->on_skip_transaction(height, txid, tx); return; } @@ -864,7 +872,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s td.m_internal_output_index = o; td.m_global_output_index = o_indices[o]; td.m_tx = (const cryptonote::transaction_prefix&)tx; - td.m_txid = txid(); + td.m_txid = txid; td.m_key_image = ki[o]; td.m_key_image_known = !m_watch_only; td.m_amount = tx.vout[o].amount; @@ -888,9 +896,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s set_unspent(m_transfers.size()-1); m_key_images[td.m_key_image] = m_transfers.size()-1; m_pub_keys[in_ephemeral[o].pub] = m_transfers.size()-1; - LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid()); + LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) - m_callback->on_money_received(height, tx, td.m_amount); + m_callback->on_money_received(height, txid, tx, td.m_amount); } } else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount) @@ -915,7 +923,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s td.m_internal_output_index = o; td.m_global_output_index = o_indices[o]; td.m_tx = (const cryptonote::transaction_prefix&)tx; - td.m_txid = txid(); + td.m_txid = txid; td.m_amount = tx.vout[o].amount; td.m_pk_index = pk_index - 1; if (td.m_amount == 0) @@ -937,9 +945,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s THROW_WALLET_EXCEPTION_IF(td.get_public_key() != in_ephemeral[o].pub, error::wallet_internal_error, "Inconsistent public keys"); THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status"); - LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid()); + LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) - m_callback->on_money_received(height, tx, td.m_amount); + m_callback->on_money_received(height, txid, tx, td.m_amount); } } } @@ -967,17 +975,17 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s tx_money_spent_in_ins += amount; if (!pool) { - LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid()); + LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid); set_spent(it->second, height); if (0 != m_callback) - m_callback->on_money_spent(height, tx, amount, tx); + m_callback->on_money_spent(height, txid, tx, amount, tx); } } } if (tx_money_spent_in_ins > 0) { - process_outgoing(tx, height, ts, tx_money_spent_in_ins, tx_money_got_in_outs); + process_outgoing(txid, tx, height, ts, tx_money_spent_in_ins, tx_money_got_in_outs); } uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0; @@ -1023,7 +1031,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s } payment_details payment; - payment.m_tx_hash = txid(); + payment.m_tx_hash = txid; payment.m_amount = received; payment.m_block_height = height; payment.m_unlock_time = tx.unlock_time; @@ -1031,7 +1039,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s if (pool) { m_unconfirmed_payments.emplace(payment_id, payment); if (0 != m_callback) - m_callback->on_unconfirmed_money_received(height, tx, payment.m_amount); + m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount); } else m_payments.emplace(payment_id, payment); @@ -1039,12 +1047,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s } } //---------------------------------------------------------------------------------------------------- -void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t height) +void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height) { if (m_unconfirmed_txs.empty()) return; - crypto::hash txid = get_transaction_hash(tx); auto unconf_it = m_unconfirmed_txs.find(txid); if(unconf_it != m_unconfirmed_txs.end()) { if (store_tx_info()) { @@ -1060,9 +1067,8 @@ void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t he } } //---------------------------------------------------------------------------------------------------- -void wallet2::process_outgoing(const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received) +void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received) { - crypto::hash txid = get_transaction_hash(tx); std::pair<std::unordered_map<crypto::hash, confirmed_transfer_details>::iterator, bool> entry = m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details())); // fill with the info we know, some info might already be there if (entry.second) @@ -1105,16 +1111,19 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height) { TIME_MEASURE_START(miner_tx_handle_time); - process_new_transaction(b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false); + process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false); TIME_MEASURE_FINISH(miner_tx_handle_time); TIME_MEASURE_START(txs_handle_time); - for(auto& txblob: bche.txs) + THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block"); + size_t idx = 0; + for (const auto& txblob: bche.txs) { cryptonote::transaction tx; - bool r = parse_and_validate_tx_from_blob(txblob, tx); + bool r = parse_and_validate_tx_base_from_blob(txblob, tx); THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); - process_new_transaction(tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false); + process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false); + ++idx; } TIME_MEASURE_FINISH(txs_handle_time); LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); @@ -1170,6 +1179,34 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); req.block_ids = short_chain_history; + uint32_t rpc_version; + boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version); + // no error + if (!!result) + { + // empty string -> not connection + THROW_WALLET_EXCEPTION_IF(result->empty(), tools::error::no_connection_to_daemon, "getversion"); + THROW_WALLET_EXCEPTION_IF(*result == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "getversion"); + if (*result != CORE_RPC_STATUS_OK) + { + MDEBUG("Cannot determined daemon RPC version, not asking for pruned blocks"); + req.prune = false; // old daemon + } + } + else + { + if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 7)) + { + MDEBUG("Daemon is recent enough, asking for pruned blocks"); + req.prune = true; + } + else + { + MDEBUG("Daemon is too old, not asking for pruned blocks"); + req.prune = false; + } + } + req.start_height = start_height; m_daemon_rpc_mutex.lock(); bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout); @@ -1352,25 +1389,28 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei //---------------------------------------------------------------------------------------------------- void wallet2::update_pool_state() { + MDEBUG("update_pool_state start"); + // get the pool state - cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::request req; - cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::response res; + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request req; + cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response res; m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/get_transaction_pool", req, res, m_http_client, rpc_timeout); + bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool"); - THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool"); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool_hashes.bin"); + THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool_hashes.bin"); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); + MDEBUG("update_pool_state got pool"); // remove any pending tx that's not in the pool std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin(); while (it != m_unconfirmed_txs.end()) { - const std::string txid = epee::string_tools::pod_to_hex(it->first); + const crypto::hash &txid = it->first; bool found = false; - for (auto it2: res.transactions) + for (const auto &it2: res.tx_hashes) { - if (it2.id_hash == txid) + if (it2 == txid) { found = true; break; @@ -1415,16 +1455,17 @@ void wallet2::update_pool_state() } } } + MDEBUG("update_pool_state done first loop"); // remove pool txes to us that aren't in the pool anymore std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin(); while (uit != m_unconfirmed_payments.end()) { - const std::string txid = string_tools::pod_to_hex(uit->first); + const crypto::hash &txid = uit->second.m_tx_hash; bool found = false; - for (auto it2: res.transactions) + for (const auto &it2: res.tx_hashes) { - if (it2.id_hash == txid) + if (it2 == txid) { found = true; break; @@ -1433,102 +1474,122 @@ void wallet2::update_pool_state() auto pit = uit++; if (!found) { + MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool"); m_unconfirmed_payments.erase(pit); } } + MDEBUG("update_pool_state done second loop"); - // add new pool txes to us - for (auto it: res.transactions) + // gather txids of new pool txes to us + std::vector<crypto::hash> txids; + for (const auto &txid: res.tx_hashes) { - cryptonote::blobdata txid_data; - if(epee::string_tools::parse_hexstr_to_binbuff(it.id_hash, txid_data)) + if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end()) + { + LOG_PRINT_L2("Already seen " << txid << ", skipped"); + continue; + } + if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end()) { - const crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); - if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end()) + LOG_PRINT_L1("Found new pool tx: " << txid); + bool found = false; + for (const auto &i: m_unconfirmed_txs) { - LOG_PRINT_L1("Found new pool tx: " << txid); - bool found = false; - for (const auto &i: m_unconfirmed_txs) + if (i.first == txid) { - if (i.first == txid) - { - found = true; - break; - } + found = true; + break; } - if (!found) + } + if (!found) + { + // not one of those we sent ourselves + txids.push_back(txid); + } + else + { + LOG_PRINT_L1("We sent that one"); + } + } + else + { + LOG_PRINT_L1("Already saw that one, it's for us"); + } + } + + // get those txes + if (!txids.empty()) + { + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res; + for (const auto &txid: txids) + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); + MDEBUG("asking for " << txids.size() << " transactions"); + req.decode_as_json = false; + m_daemon_rpc_mutex.lock(); + bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + MDEBUG("Got " << r << " and " << res.status); + if (r && res.status == CORE_RPC_STATUS_OK) + { + if (res.txs.size() == txids.size()) + { + size_t n = 0; + for (const auto &txid: txids) { - // not one of those we sent ourselves - cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; - cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res; - req.txs_hashes.push_back(it.id_hash); - req.decode_as_json = false; - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout); - m_daemon_rpc_mutex.unlock(); - if (r && res.status == CORE_RPC_STATUS_OK) + // might have just been put in a block + if (res.txs[n].in_pool) { - if (res.txs.size() == 1) + cryptonote::transaction tx; + cryptonote::blobdata bd; + crypto::hash tx_hash, tx_prefix_hash; + if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[n].as_hex, bd)) { - // might have just been put in a block - if (res.txs[0].in_pool) + if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash)) { - cryptonote::transaction tx; - cryptonote::blobdata bd; - crypto::hash tx_hash, tx_prefix_hash; - if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[0].as_hex, bd)) + if (tx_hash == txid) { - if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash)) - { - if (tx_hash == txid) - { - process_new_transaction(tx, std::vector<uint64_t>(), 0, time(NULL), false, true); - } - else - { - LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool"); - } - } - else + process_new_transaction(txid, tx, std::vector<uint64_t>(), 0, time(NULL), false, true); + m_scanned_pool_txs[0].insert(txid); + if (m_scanned_pool_txs[0].size() > 5000) { - LOG_PRINT_L0("failed to validate transaction from daemon"); + std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]); + m_scanned_pool_txs[0].clear(); } } else { - LOG_PRINT_L0("Failed to parse tx " << txid); + LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool"); } } else { - LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more"); + LOG_PRINT_L0("failed to validate transaction from daemon"); } } else { - LOG_PRINT_L0("Expected 1 tx, got " << res.txs.size()); + LOG_PRINT_L0("Failed to parse tx " << txid); } } else { - LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << res.status); + LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more"); } - } - else - { - LOG_PRINT_L1("We sent that one"); + ++n; } } else { - LOG_PRINT_L1("Already saw that one"); + LOG_PRINT_L0("Expected " << txids.size() << " tx(es), got " << res.txs.size()); } } else { - LOG_PRINT_L0("Failed to parse txid"); + LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << res.status); } } + MDEBUG("update_pool_state end"); } //---------------------------------------------------------------------------------------------------- void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history) @@ -1865,6 +1926,18 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p value2.SetInt(m_ask_password ? 1 :0); json.AddMember("ask_password", value2, json.GetAllocator()); + value2.SetUint(m_min_output_count); + json.AddMember("min_output_count", value2, json.GetAllocator()); + + value2.SetUint64(m_min_output_value); + json.AddMember("min_output_value", value2, json.GetAllocator()); + + value2.SetInt(cryptonote::get_default_decimal_point()); + json.AddMember("default_decimal_point", value2, json.GetAllocator()); + + value2.SetInt(m_merge_destinations ? 1 :0); + json.AddMember("merge_destinations", value2, json.GetAllocator()); + // Serialize the JSON object rapidjson::StringBuffer buffer; rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); @@ -1933,6 +2006,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa m_refresh_type = RefreshType::RefreshDefault; m_confirm_missing_payment_id = true; m_ask_password = true; + m_min_output_count = 0; + m_min_output_value = 0; + m_merge_destinations = false; } else { @@ -1995,6 +2071,14 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa m_confirm_missing_payment_id = field_confirm_missing_payment_id; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, int, Int, false, true); m_ask_password = field_ask_password; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT); + cryptonote::set_default_decimal_point(field_default_decimal_point); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_count, uint32_t, Uint, false, 0); + m_min_output_count = field_min_output_count; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_value, uint64_t, Uint64, false, 0); + m_min_output_value = field_min_output_value; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, merge_destinations, int, Int, false, false); + m_merge_destinations = field_merge_destinations; } const cryptonote::account_keys& keys = m_account.get_keys(); @@ -2259,6 +2343,16 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& return false; } //---------------------------------------------------------------------------------------------------- +void wallet2::set_default_decimal_point(unsigned int decimal_point) +{ + cryptonote::set_default_decimal_point(decimal_point); +} +//---------------------------------------------------------------------------------------------------- +unsigned int wallet2::get_default_decimal_point() const +{ + return cryptonote::get_default_decimal_point(); +} +//---------------------------------------------------------------------------------------------------- bool wallet2::prepare_file_names(const std::string& file_path) { do_prepare_file_names(file_path, m_keys_file, m_wallet_file); @@ -2271,6 +2365,7 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout) if(!m_http_client.is_connected()) { + m_node_rpc_proxy.invalidate(); if (!m_http_client.connect(std::chrono::milliseconds(timeout))) return false; } @@ -2283,7 +2378,11 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout) req_t.id = epee::serialization::storage_entry(0); req_t.method = "get_version"; bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); - if (!r || resp_t.result.status != CORE_RPC_STATUS_OK) + if(!r) { + *version = 0; + return false; + } + if (resp_t.result.status != CORE_RPC_STATUS_OK) *version = 0; else *version = resp_t.result.version; @@ -2590,28 +2689,31 @@ void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2: //---------------------------------------------------------------------------------------------------- void wallet2::rescan_spent() { - std::vector<std::string> key_images; - - // make a list of key images for all our outputs - for (size_t i = 0; i < m_transfers.size(); ++i) - { - const transfer_details& td = m_transfers[i]; - key_images.push_back(string_tools::pod_to_hex(td.m_key_image)); + // This is RPC call that can take a long time if there are many outputs, + // so we call it several times, in stripes, so we don't time out spuriously + std::vector<int> spent_status; + spent_status.reserve(m_transfers.size()); + const size_t chunk_size = 1000; + for (size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size) + { + const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset); + MDEBUG("Calling is_key_image_spent on " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size()); + COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req); + COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp); + for (size_t n = start_offset; n < start_offset + n_outputs; ++n) + req.key_images.push_back(string_tools::pod_to_hex(m_transfers[n].m_key_image)); + m_daemon_rpc_mutex.lock(); + bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status); + THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error, + "daemon returned wrong response for is_key_image_spent, wrong amounts count = " + + std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs)); + std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status)); } - COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req); - COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp); - req.key_images = key_images; - m_daemon_rpc_mutex.lock(); - bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status); - THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != key_images.size(), error::wallet_internal_error, - "daemon returned wrong response for is_key_image_spent, wrong amounts count = " + - std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(key_images.size())); - // update spent status for (size_t i = 0; i < m_transfers.size(); ++i) { @@ -2619,7 +2721,7 @@ void wallet2::rescan_spent() // a view wallet may not know about key images if (!td.m_key_image_known) continue; - if (td.m_spent != (daemon_resp.spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT)) + if (td.m_spent != (spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT)) { if (td.m_spent) { @@ -3271,20 +3373,35 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal return true; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::get_fee_multiplier(uint32_t priority, bool use_new_fee) const +uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const { static const uint64_t old_multipliers[3] = {1, 2, 3}; static const uint64_t new_multipliers[3] = {1, 20, 166}; + static const uint64_t newer_multipliers[4] = {1, 4, 20, 166}; - // 0 -> default (here, x1) + // 0 -> default (here, x1 till fee algorithm 2, x4 from it) if (priority == 0) priority = m_default_priority; if (priority == 0) - priority = 1; + { + if (fee_algorithm >= 2) + priority = 2; + else + priority = 1; + } - // 1 to 3 are allowed as priorities - if (priority >= 1 && priority <= 3) - return (use_new_fee ? new_multipliers : old_multipliers)[priority-1]; + // 1 to 3/4 are allowed as priorities + uint32_t max_priority = (fee_algorithm >= 2) ? 4 : 3; + if (priority >= 1 && priority <= max_priority) + { + switch (fee_algorithm) + { + case 0: return old_multipliers[priority-1]; + case 1: return new_multipliers[priority-1]; + case 2: return newer_multipliers[priority-1]; + default: THROW_WALLET_EXCEPTION_IF (true, error::invalid_priority); + } + } THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority); return 1; @@ -3309,17 +3426,26 @@ uint64_t wallet2::get_per_kb_fee() return get_dynamic_per_kb_fee_estimate(); } //---------------------------------------------------------------------------------------------------- +int wallet2::get_fee_algorithm() +{ + // changes at v3 and v5 + if (use_fork_rules(5, 0)) + return 2; + if (use_fork_rules(3, -720 * 14)) + return 1; + return 0; +} +//---------------------------------------------------------------------------------------------------- // separated the call(s) to wallet2::transfer into their own function // // this function will make multiple calls to wallet2::transfer if multiple // transactions will be required std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon) { - const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, trusted_daemon); + const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true, trusted_daemon); - const bool use_new_fee = use_fork_rules(3, -720 * 14); const uint64_t fee_per_kb = get_per_kb_fee(); - const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee); + const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); // failsafe split attempt counter size_t attempt_count = 0; @@ -3438,7 +3564,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> req_t.params.amounts.resize(std::distance(req_t.params.amounts.begin(), end)); req_t.params.unlocked = true; req_t.params.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE; - bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected"); THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); @@ -3525,7 +3651,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> uint64_t i; if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with { - // equiprobable distribution over the recent outs + // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53); double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); i = (uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs; @@ -3984,6 +4110,14 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs) return size; } +static size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs) +{ + if (use_rct) + return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1); + else + return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES; +} + std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money) const { std::vector<size_t> picks; @@ -4044,7 +4178,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money) co return picks; } -static bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) +bool wallet2::should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const { if (!use_rct) return false; @@ -4052,9 +4186,52 @@ static bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const return false; if (unused_dust_indices.empty() && unused_transfers_indices.empty()) return false; + // we want at least one free rct output to avoid a corner case where + // we'd choose a non rct output which doesn't have enough "siblings" + // value-wise on the chain, and thus can't be mixed + bool found = false; + for (auto i: unused_dust_indices) + { + if (m_transfers[i].is_rct()) + { + found = true; + break; + } + } + if (!found) for (auto i: unused_transfers_indices) + { + if (m_transfers[i].is_rct()) + { + found = true; + break; + } + } + if (!found) + return false; return true; } +std::vector<size_t> wallet2::get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const +{ + std::vector<size_t> indices; + for (size_t n: unused_dust_indices) + if (m_transfers[n].is_rct()) + indices.push_back(n); + for (size_t n: unused_transfers_indices) + if (m_transfers[n].is_rct()) + indices.push_back(n); + return indices; +} + +static uint32_t get_count_above(const std::vector<wallet2::transfer_details> &transfers, const std::vector<size_t> &indices, uint64_t threshold) +{ + uint32_t count = 0; + for (size_t idx: indices) + if (transfers[idx].amount() >= threshold) + ++count; + return count; +} + // Another implementation of transaction creation that is hopefully better // While there is anything left to pay, it goes through random outputs and tries // to fill the next destination/amount. If it fully fills it, it will use the @@ -4083,13 +4260,23 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp pending_tx ptx; size_t bytes; - void add(const account_public_address &addr, uint64_t amount) { - std::vector<cryptonote::tx_destination_entry>::iterator i; - i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); }); - if (i == dsts.end()) - dsts.push_back(tx_destination_entry(amount,addr)); - else + void add(const account_public_address &addr, uint64_t amount, unsigned int original_output_index, bool merge_destinations) { + if (merge_destinations) + { + std::vector<cryptonote::tx_destination_entry>::iterator i; + i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); }); + if (i == dsts.end()) + dsts.push_back(tx_destination_entry(0,addr)); i->amount += amount; + } + else + { + THROW_WALLET_EXCEPTION_IF(original_output_index > dsts.size(), error::wallet_internal_error, "original_output_index too large"); + if (original_output_index == dsts.size()) + dsts.push_back(tx_destination_entry(0,addr)); + THROW_WALLET_EXCEPTION_IF(memcmp(&dsts[original_output_index].addr, &addr, sizeof(addr)), error::wallet_internal_error, "Mismatched destination address"); + dsts[original_output_index].amount += amount; + } } }; std::vector<TX> txes; @@ -4098,9 +4285,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit(); const bool use_rct = use_fork_rules(4, 0); - const bool use_new_fee = use_fork_rules(3, -720 * 14); const uint64_t fee_per_kb = get_per_kb_fee(); - const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee); + const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); // throw if attempting a transaction with no destinations THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); @@ -4120,8 +4306,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination); // gather all our dust and non dust outputs - const std::vector<size_t> unused_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, trusted_daemon); - for (size_t i: unused_indices) + for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td)) @@ -4180,6 +4365,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // - we have something to send // - or we need to gather more fee // - or we have just one input in that tx, which is rct (to try and make all/most rct txes 2/2) + unsigned int original_output_index = 0; while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), unused_transfers_indices, unused_dust_indices)) { TX &tx = txes.back(); @@ -4202,22 +4388,33 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc) // This could be more clever, but maybe at the cost of making probabilistic inferences easier size_t idx; - if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) + if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) { // the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too - idx = pop_best_value(unused_dust_indices.empty() ? unused_transfers_indices : unused_dust_indices, tx.selected_transfers, true); - else if (!prefered_inputs.empty()) { - idx = pop_back(prefered_inputs); - pop_if_present(unused_transfers_indices, idx); - pop_if_present(unused_dust_indices, idx); + std::vector<size_t> indices = get_only_rct(unused_dust_indices, unused_transfers_indices); + idx = pop_best_value(indices, tx.selected_transfers, true); + + // we might not want to add it if it's a large output and we don't have many left + if (m_transfers[idx].amount() >= m_min_output_value) { + if (get_count_above(m_transfers, unused_transfers_indices, m_min_output_value) < m_min_output_count) { + LOG_PRINT_L2("Second output was not strictly needed, and we're running out of outputs above " << print_money(m_min_output_value) << ", not adding"); + break; + } + } // since we're trying to add a second output which is not strictly needed, // we only add it if it's unrelated enough to the first one float relatedness = get_output_relatedness(m_transfers[idx], m_transfers[tx.selected_transfers.front()]); if (relatedness > SECOND_OUTPUT_RELATEDNESS_THRESHOLD) { - LOG_PRINT_L2("Second outout was not strictly needed, and relatedness " << relatedness << ", not adding"); + LOG_PRINT_L2("Second output was not strictly needed, and relatedness " << relatedness << ", not adding"); break; } + pop_if_present(unused_transfers_indices, idx); + pop_if_present(unused_dust_indices, idx); + } else if (!prefered_inputs.empty()) { + idx = pop_back(prefered_inputs); + pop_if_present(unused_transfers_indices, idx); + pop_if_present(unused_dust_indices, idx); } else idx = pop_best_value(unused_transfers_indices.empty() ? unused_dust_indices : unused_transfers_indices, tx.selected_transfers); @@ -4239,22 +4436,23 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } else { - while (!dsts.empty() && dsts[0].amount <= available_amount) + while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can fully pay that destination LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].addr) << " for " << print_money(dsts[0].amount)); - tx.add(dsts[0].addr, dsts[0].amount); + tx.add(dsts[0].addr, dsts[0].amount, original_output_index, m_merge_destinations); available_amount -= dsts[0].amount; dsts[0].amount = 0; pop_index(dsts, 0); + ++original_output_index; } - if (available_amount > 0 && !dsts.empty()) { + if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) { // we can partially fill that destination LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].addr) << " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); - tx.add(dsts[0].addr, available_amount); + tx.add(dsts[0].addr, available_amount, original_output_index, m_merge_destinations); dsts[0].amount -= available_amount; available_amount = 0; } @@ -4271,11 +4469,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } else { - size_t estimated_rct_tx_size; - if (use_rct) - estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1); - else - estimated_rct_tx_size = tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES; + const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()); try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); } @@ -4387,7 +4581,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp return ptx_vector; } -std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon) +std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon) { std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_dust_indices; @@ -4399,10 +4593,13 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono const transfer_details& td = m_transfers[i]; if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td)) { - if (td.is_rct() || is_valid_decomposed_amount(td.amount())) - unused_transfers_indices.push_back(i); - else - unused_dust_indices.push_back(i); + if (below == 0 || td.amount() < below) + { + if (td.is_rct() || is_valid_decomposed_amount(td.amount())) + unused_transfers_indices.push_back(i); + else + unused_dust_indices.push_back(i); + } } } @@ -4425,9 +4622,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton std::vector<std::vector<get_outs_entry>> outs; const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); - const bool use_new_fee = use_fork_rules(3, -720 * 14); const uint64_t fee_per_kb = get_per_kb_fee(); - const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee); + const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs"); @@ -4464,11 +4660,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton // here, check if we need to sent tx and start a new one LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " << upper_transaction_size_limit); - size_t estimated_rct_tx_size; - if (use_rct) - estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1); - else - estimated_rct_tx_size = tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES; + const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1); bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); if (try_tx) { @@ -4588,7 +4780,7 @@ uint64_t wallet2::get_upper_tranaction_size_limit() { if (m_upper_transaction_size_limit > 0) return m_upper_transaction_size_limit; - uint64_t full_reward_zone = use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; return ((full_reward_zone * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; } //---------------------------------------------------------------------------------------------------- @@ -4614,7 +4806,7 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector() for (const auto &td: m_transfers) { if (!td.m_spent) - set.insert(td.amount()); + set.insert(td.is_rct() ? 0 : td.amount()); } std::vector<uint64_t> vector; vector.reserve(set.size()); @@ -4625,7 +4817,7 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector() return vector; } //---------------------------------------------------------------------------------------------------- -std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool trusted_daemon) +std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon) { epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request> req_t = AUTO_VAL_INIT(req_t); epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response, std::string> resp_t = AUTO_VAL_INIT(resp_t); @@ -4638,9 +4830,9 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co req_t.params.min_count = count; req_t.params.max_count = 0; req_t.params.unlocked = unlocked; - bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_unmixable_outputs"); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram"); THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.result.status); @@ -4650,10 +4842,10 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co mixable.insert(i.amount); } - return select_available_outputs([mixable, atleast](const transfer_details &td) { - if (td.is_rct()) + return select_available_outputs([mixable, atleast, allow_rct](const transfer_details &td) { + if (!allow_rct && td.is_rct()) return false; - const uint64_t amount = td.amount(); + const uint64_t amount = td.is_rct() ? 0 : td.amount(); if (atleast) { if (mixable.find(amount) != mixable.end()) return true; @@ -4677,7 +4869,7 @@ uint64_t wallet2::get_num_rct_outputs() req_t.params.amounts.push_back(0); req_t.params.min_count = 0; req_t.params.max_count = 0; - bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs"); THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram"); @@ -4698,14 +4890,14 @@ std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_dae { // request all outputs with less than 3 instances const size_t min_mixin = use_fork_rules(5, 10) ? 4 : 2; // v5 increases min mixin from 2 to 4 - return select_available_outputs_from_histogram(min_mixin + 1, false, true, trusted_daemon); + return select_available_outputs_from_histogram(min_mixin + 1, false, true, false, trusted_daemon); } //---------------------------------------------------------------------------------------------------- std::vector<size_t> wallet2::select_available_mixable_outputs(bool trusted_daemon) { // request all outputs with at least 3 instances, so we can use mixin 2 with const size_t min_mixin = use_fork_rules(5, 10) ? 4 : 2; // v5 increases min mixin from 2 to 4 - return select_available_outputs_from_histogram(min_mixin + 1, true, true, trusted_daemon); + return select_available_outputs_from_histogram(min_mixin + 1, true, true, true, trusted_daemon); } //---------------------------------------------------------------------------------------------------- std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bool trusted_daemon) @@ -4714,7 +4906,6 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2 tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD); - const bool use_new_fee = use_fork_rules(3, -720 * 14); const uint64_t fee_per_kb = get_per_kb_fee(); // may throw @@ -5409,7 +5600,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui height_mid, height_max }; - bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client); + bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client, rpc_timeout); if (!r || res.status != CORE_RPC_STATUS_OK) { std::ostringstream oss; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 567292d30..03c6a431b 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -39,13 +39,14 @@ #include <atomic> #include "include_base_utils.h" -#include "cryptonote_core/account.h" -#include "cryptonote_core/account_boost_serialization.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/account_boost_serialization.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "net/http_client.h" #include "storages/http_abstract_invoke.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include "common/unordered_containers_boost_serialization.h" #include "crypto/chacha8.h" #include "crypto/hash.h" @@ -53,7 +54,7 @@ #include "ringct/rctOps.h" #include "wallet_errors.h" -#include "password_container.h" +#include "common/password.h" #include "node_rpc_proxy.h" #include <iostream> @@ -69,10 +70,10 @@ namespace tools { public: virtual void on_new_block(uint64_t height, const cryptonote::block& block) {} - virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) {} - virtual void on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) {} - virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) {} - virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) {} + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) {} + virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) {} + virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) {} + virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {} virtual ~i_wallet2_callback() {} }; @@ -104,7 +105,7 @@ namespace tools }; private: - wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} + wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} public: static const char* tr(const char* str); @@ -113,7 +114,7 @@ namespace tools static void init_options(boost::program_options::options_description& desc_params); //! \return Password retrieved from prompt. Logs error on failure. - static boost::optional<password_container> password_prompt(const bool is_new_wallet); + static boost::optional<password_container> password_prompt(const bool new_password); //! Uses stdin and stdout. Returns a wallet2 if no errors. static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file); @@ -125,7 +126,11 @@ namespace tools //! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors. static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm); - wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} + //! Just parses variables. + static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm); + + wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} + struct transfer_details { uint64_t m_block_height; @@ -343,7 +348,8 @@ namespace tools // into account the current median block size rather than // the minimum block size. bool deinit(); - bool init(std::string daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = 0); + bool init(std::string daemon_address = "http://localhost:8080", + boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0); void stop() { m_run.store(false, std::memory_order_relaxed); } @@ -408,7 +414,7 @@ namespace tools bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL); std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); - std::vector<wallet2::pending_tx> create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); + std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon); std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon); bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000); @@ -454,7 +460,14 @@ namespace tools a & m_tx_notes; if(ver < 13) return; - a & m_unconfirmed_payments; + if (ver < 17) + { + // we're loading an old version, where m_unconfirmed_payments was a std::map + std::unordered_map<crypto::hash, payment_details> m; + a & m; + for (std::unordered_map<crypto::hash, payment_details>::const_iterator i = m.begin(); i != m.end(); ++i) + m_unconfirmed_payments.insert(*i); + } if(ver < 14) return; if(ver < 15) @@ -473,6 +486,13 @@ namespace tools if(ver < 16) return; a & m_address_book; + if(ver < 17) + return; + a & m_unconfirmed_payments; + if(ver < 18) + return; + a & m_scanned_pool_txs[0]; + a & m_scanned_pool_txs[1]; } /*! @@ -508,6 +528,14 @@ namespace tools void confirm_missing_payment_id(bool always) { m_confirm_missing_payment_id = always; } bool ask_password() const { return m_ask_password; } void ask_password(bool always) { m_ask_password = always; } + void set_default_decimal_point(unsigned int decimal_point); + unsigned int get_default_decimal_point() const; + void set_min_output_count(uint32_t count) { m_min_output_count = count; } + uint32_t get_min_output_count() const { return m_min_output_count; } + void set_min_output_value(uint64_t value) { m_min_output_value = value; } + uint64_t get_min_output_value() const { return m_min_output_value; } + void merge_destinations(bool merge) { m_merge_destinations = merge; } + bool merge_destinations() const { return m_merge_destinations; } bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const; @@ -523,17 +551,19 @@ namespace tools void get_hard_fork_info(uint8_t version, uint64_t &earliest_height); bool use_fork_rules(uint8_t version, int64_t early_blocks = 0); + int get_fee_algorithm(); std::string get_wallet_file() const; std::string get_keys_file() const; std::string get_daemon_address() const; + const boost::optional<epee::net_utils::http::login>& get_daemon_login() const { return m_daemon_login; } uint64_t get_daemon_blockchain_height(std::string& err); uint64_t get_daemon_blockchain_target_height(std::string& err); /*! * \brief Calculates the approximate blockchain height from current date/time. */ uint64_t get_approximate_blockchain_height() const; - std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool trusted_daemon); + std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon); std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f); std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon); std::vector<size_t> select_available_mixable_outputs(bool trusted_daemon); @@ -582,7 +612,7 @@ namespace tools * \param password Password of wallet file */ bool load_keys(const std::string& keys_file_name, const std::string& password); - void process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool); + void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices); void detach_blockchain(uint64_t height); void get_short_chain_history(std::list<crypto::hash>& ids) const; @@ -595,8 +625,8 @@ namespace tools void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<size_t>& selected_transfers, bool trusted_daemon); bool prepare_file_names(const std::string& file_path); - void process_unconfirmed(const cryptonote::transaction& tx, uint64_t height); - void process_outgoing(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received); + void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height); + void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received); void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount); void generate_genesis(cryptonote::block& b); void check_genesis(const crypto::hash& genesis_hash) const; //throws @@ -607,7 +637,7 @@ namespace tools void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; uint64_t get_upper_tranaction_size_limit(); std::vector<uint64_t> get_unspent_amounts_vector(); - uint64_t get_fee_multiplier(uint32_t priority, bool use_new_fee) const; + uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm) const; uint64_t get_dynamic_per_kb_fee_estimate(); uint64_t get_per_kb_fee(); float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const; @@ -617,8 +647,11 @@ namespace tools void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count); bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; + bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const; + std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const; cryptonote::account_base m_account; + boost::optional<epee::net_utils::http::login> m_daemon_login; std::string m_daemon_address; std::string m_wallet_file; std::string m_keys_file; @@ -627,7 +660,7 @@ namespace tools std::atomic<uint64_t> m_local_bc_height; //temporary workaround std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs; std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs; - std::unordered_map<crypto::hash, payment_details> m_unconfirmed_payments; + std::unordered_multimap<crypto::hash, payment_details> m_unconfirmed_payments; std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys; transfer_container m_transfers; @@ -659,10 +692,14 @@ namespace tools uint64_t m_refresh_from_block_height; bool m_confirm_missing_payment_id; bool m_ask_password; + uint32_t m_min_output_count; + uint64_t m_min_output_value; + bool m_merge_destinations; NodeRPCProxy m_node_rpc_proxy; + std::unordered_set<crypto::hash> m_scanned_pool_txs[2]; }; } -BOOST_CLASS_VERSION(tools::wallet2, 16) +BOOST_CLASS_VERSION(tools::wallet2, 18) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 7) BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6) diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index 883da7da2..17d0caf7d 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -296,7 +296,10 @@ struct Wallet virtual std::string address() const = 0; virtual std::string path() const = 0; virtual bool testnet() const = 0; - + //! returns current hard fork info + virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; + //! check if hard fork rules should be used + virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0; /*! * \brief integratedAddress - returns integrated address for current wallet address and given payment_id. * if passed "payment_id" param is an empty string or not-valid payment id string @@ -340,7 +343,7 @@ struct Wallet * \param upper_transaction_size_limit * \return - true on success */ - virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit) = 0; + virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username = "", const std::string &daemon_password = "") = 0; /*! * \brief createWatchOnly - Creates a watch only wallet @@ -428,6 +431,9 @@ struct Wallet static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); static std::string paymentIdFromAddress(const std::string &str, bool testnet); static uint64_t maximumAllowedAmount(); + // Easylogger wrapper + static void init(const char *argv0, const char *default_log_base_name); + static void debug(const std::string &str); /** * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) @@ -683,9 +689,6 @@ struct WalletManager //! returns current mining hash rate (0 if not mining) virtual double miningHashRate() const = 0; - //! returns current hard fork info - virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; - //! returns current block target virtual uint64_t blockTarget() const = 0; @@ -693,13 +696,16 @@ struct WalletManager virtual bool isMining() const = 0; //! starts mining with the set number of threads - virtual bool startMining(const std::string &address, uint32_t threads = 1) = 0; + virtual bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true) = 0; //! stops mining virtual bool stopMining() = 0; //! resolves an OpenAlias address to a monero address virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0; + + //! checks for an update and returns version, hash and url + static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir); }; diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 12799f613..1508f3791 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -73,6 +73,7 @@ namespace wallet_args const char* const usage, boost::program_options::options_description desc_params, const boost::program_options::positional_options_description& positional_options, + const char *default_log_name, bool log_to_console) { @@ -85,6 +86,7 @@ namespace wallet_args const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""}; const command_line::arg_descriptor<uint32_t> arg_max_concurrency = {"max-concurrency", wallet_args::tr("Max number of threads to use for a parallel job"), DEFAULT_MAX_CONCURRENCY}; const command_line::arg_descriptor<std::string> arg_log_file = {"log-file", wallet_args::tr("Specify log file"), ""}; + const command_line::arg_descriptor<std::string> arg_config_file = {"config-file", wallet_args::tr("Config file"), "", true}; std::string lang = i18n_get_language(); @@ -100,6 +102,7 @@ namespace wallet_args command_line::add_arg(desc_params, arg_log_file, ""); command_line::add_arg(desc_params, arg_log_level); command_line::add_arg(desc_params, arg_max_concurrency); + command_line::add_arg(desc_params, arg_config_file); i18n_set_language("translations", "monero", lang); @@ -108,43 +111,58 @@ namespace wallet_args po::variables_map vm; bool r = command_line::handle_error_helper(desc_all, [&]() { - po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm); + auto parser = po::command_line_parser(argc, argv).options(desc_all).positional(positional_options); + po::store(parser.run(), vm); - if (command_line::get_arg(vm, command_line::arg_help)) - { - tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; - tools::msg_writer() << wallet_args::tr("Usage:") << ' ' << usage; - tools::msg_writer() << desc_all; - return false; - } - else if (command_line::get_arg(vm, command_line::arg_version)) + if(command_line::has_arg(vm, arg_config_file)) { - tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; - return false; + std::string config = command_line::get_arg(vm, arg_config_file); + bf::path config_path(config); + boost::system::error_code ec; + if (bf::exists(config_path, ec)) + { + po::store(po::parse_config_file<char>(config_path.string<std::string>().c_str(), desc_params), vm); + } + else + { + tools::fail_msg_writer() << wallet_args::tr("Can't find config file ") << config; + return false; + } } - auto parser = po::command_line_parser(argc, argv).options(desc_params).positional(positional_options); - po::store(parser.run(), vm); po::notify(vm); return true; }); if (!r) return boost::none; - if(command_line::has_arg(vm, arg_max_concurrency)) - tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); - std::string log_path; if (!vm["log-file"].defaulted()) log_path = command_line::get_arg(vm, arg_log_file); else - log_path = mlog_get_default_log_path("monero-wallet-cli.log"); + log_path = mlog_get_default_log_path(default_log_name); mlog_configure(log_path, log_to_console); if (!vm["log-level"].defaulted()) { mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); } + if (command_line::get_arg(vm, command_line::arg_help)) + { + tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + tools::msg_writer() << wallet_args::tr("Usage:") << ' ' << usage; + tools::msg_writer() << desc_all; + return boost::none; + } + else if (command_line::get_arg(vm, command_line::arg_version)) + { + tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + return boost::none; + } + + if(command_line::has_arg(vm, arg_max_concurrency)) + tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); + tools::scoped_message_writer(epee::console_color_white, true) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; if (!vm["log-level"].defaulted()) diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h index e0719d203..cf23ffded 100644 --- a/src/wallet/wallet_args.h +++ b/src/wallet/wallet_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -50,5 +50,5 @@ namespace wallet_args const char* const usage, boost::program_options::options_description desc_params, const boost::program_options::positional_options_description& positional_options, - bool log_to_console = false); + const char *default_log_name, bool log_to_console = false); } diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 785a72e4b..3e3578149 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -35,7 +35,7 @@ #include <string> #include <vector> -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "rpc/core_rpc_server_commands_defs.h" #include "include_base_utils.h" diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 22f5f8bb6..f2b3dcaf5 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -38,13 +38,16 @@ using namespace epee; #include "common/command_line.h" #include "common/i18n.h" #include "common/util.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/account.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/account.h" #include "wallet_rpc_server_commands_defs.h" #include "misc_language.h" #include "string_coding.h" #include "string_tools.h" #include "crypto/hash.h" +#include "mnemonics/electrum-words.h" +#include "rpc/rpc_args.h" +#include "rpc/core_rpc_server_commands_defs.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc" @@ -52,10 +55,9 @@ using namespace epee; namespace { const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; - const command_line::arg_descriptor<std::string> arg_rpc_bind_ip = {"rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"}; - const command_line::arg_descriptor<std::string> arg_rpc_login = {"rpc-login", "Specify username[:password] required for RPC connection"}; - const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC"}; - const command_line::arg_descriptor<bool> arg_confirm_external_bind = {"confirm-external-bind", "Confirm rcp-bind-ip value is NOT a loopback (local) IP"}; + const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"}; + const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", "Enable commands which rely on a trusted daemon", false}; + const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; constexpr const char default_rpc_username[] = "monero"; } @@ -68,8 +70,9 @@ namespace tools } //------------------------------------------------------------------------------------------------------------------------------ - wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w), rpc_login_filename(), m_stop(false) - {} + wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_filename(), m_stop(false), m_trusted_daemon(false) + { + } //------------------------------------------------------------------------------------------------------------------------------ wallet_rpc_server::~wallet_rpc_server() { @@ -81,12 +84,17 @@ namespace tools catch (...) {} } //------------------------------------------------------------------------------------------------------------------------------ + void wallet_rpc_server::set_wallet(wallet2 *cr) + { + m_wallet = cr; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::run() { m_stop = false; m_net_server.add_idle_handler([this](){ try { - m_wallet.refresh(); + if (m_wallet) m_wallet->refresh(); } catch (const std::exception& ex) { LOG_ERROR("Exception at while refreshing, what=" << ex.what()); } @@ -105,77 +113,83 @@ namespace tools return epee::http_server_impl_base<wallet_rpc_server, connection_context>::run(1, true); } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::init(const boost::program_options::variables_map& vm) + void wallet_rpc_server::stop() { - std::string bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); - if (!bind_ip.empty()) + if (m_wallet) { - // always parse IP here for error consistency - boost::system::error_code ec{}; - const auto parsed_ip = boost::asio::ip::address::from_string(bind_ip, ec); - if (ec) - { - LOG_ERROR(tr("Invalid IP address given for rpc-bind-ip argument")); - return false; - } + m_wallet->store(); + delete m_wallet; + m_wallet = NULL; + } + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::init(const boost::program_options::variables_map *vm) + { + auto rpc_config = cryptonote::rpc_args::process(*vm); + if (!rpc_config) + return false; + + m_vm = vm; + tools::wallet2 *walvars; + std::unique_ptr<tools::wallet2> tmpwal; - if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg_confirm_external_bind)) + if (m_wallet) + walvars = m_wallet; + else + { + tmpwal = tools::wallet2::make_dummy(*m_vm); + walvars = tmpwal.get(); + } + boost::optional<epee::net_utils::http::login> http_login{}; + std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port); + const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login); + m_trusted_daemon = command_line::get_arg(*m_vm, arg_trusted_daemon); + if (!command_line::has_arg(*m_vm, arg_trusted_daemon)) + { + if (tools::is_local_address(walvars->get_daemon_address())) { - LOG_ERROR( - tr("The rpc-bind-ip value is listening for unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --confirm-external-bind") - ); - return false; + MINFO(tr("Daemon is local, assuming trusted")); + m_trusted_daemon = true; } } - - epee::net_utils::http::login login{}; - - const bool disable_auth = command_line::get_arg(vm, arg_disable_rpc_login); - const std::string user_pass = command_line::get_arg(vm, arg_rpc_login); - const std::string bind_port = command_line::get_arg(vm, arg_rpc_bind_port); + if (command_line::has_arg(*m_vm, arg_wallet_dir)) + { + m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir); +#ifdef _WIN32 +#define MKDIR(path, mode) mkdir(path) +#else +#define MKDIR(path, mode) mkdir(path, mode) +#endif + MKDIR(m_wallet_dir.c_str(), 0700); + } if (disable_auth) { - if (!user_pass.empty()) + if (rpc_config->login) { - LOG_ERROR(tr("Cannot specify --") << arg_disable_rpc_login.name << tr(" and --") << arg_rpc_login.name); + const cryptonote::rpc_args::descriptors arg{}; + LOG_ERROR(tr("Cannot specify --") << arg_disable_rpc_login.name << tr(" and --") << arg.rpc_login.name); return false; } } else // auth enabled { - if (user_pass.empty()) + if (!rpc_config->login) { - login.username = default_rpc_username; - std::array<std::uint8_t, 16> rand_128bit{{}}; crypto::rand(rand_128bit.size(), rand_128bit.data()); - login.password = string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size()); + http_login.emplace( + default_rpc_username, + string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size()) + ); } - else // user password + else { - const auto loc = user_pass.find(':'); - login.username = user_pass.substr(0, loc); - if (loc != std::string::npos) - { - login.password = user_pass.substr(loc + 1); - } - else - { - login.password = tools::password_container::prompt(true, "RPC password").value_or( - tools::password_container{} - ).password(); - } - - if (login.username.empty() || login.password.empty()) - { - LOG_ERROR(tr("Blank username or password not permitted for RPC authenticaion")); - return false; - } + http_login.emplace( + std::move(rpc_config->login->username), std::move(rpc_config->login->password).password() + ); } - - assert(!login.username.empty()); - assert(!login.password.empty()); + assert(bool(http_login)); std::string temp = "monero-wallet-rpc." + bind_port + ".login"; const auto cookie = tools::create_private_file(temp); @@ -186,9 +200,9 @@ namespace tools } rpc_login_filename.swap(temp); // nothrow guarantee destructor cleanup temp = rpc_login_filename; - std::fputs(login.username.c_str(), cookie.get()); + std::fputs(http_login->username.c_str(), cookie.get()); std::fputc(':', cookie.get()); - std::fputs(login.password.c_str(), cookie.get()); + std::fputs(http_login->password.c_str(), cookie.get()); std::fflush(cookie.get()); if (std::ferror(cookie.get())) { @@ -198,12 +212,21 @@ namespace tools LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp); } // end auth enabled + m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login()); + m_net_server.set_threads_prefix("RPC"); return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init( - std::move(bind_port), std::move(bind_ip), std::string{}, boost::make_optional(!disable_auth, std::move(login)) + std::move(bind_port), std::move(rpc_config->bind_ip), std::move(http_login) ); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::not_open(epee::json_rpc::error& er) + { + er.code = WALLET_RPC_ERROR_CODE_NOT_OPEN; + er.message = "No wallet file"; + return false; + } + //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd) { entry.txid = string_tools::pod_to_hex(pd.m_tx_hash); @@ -214,7 +237,7 @@ namespace tools entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; entry.fee = 0; // TODO - entry.note = m_wallet.get_tx_note(pd.m_tx_hash); + entry.note = m_wallet->get_tx_note(pd.m_tx_hash); entry.type = "in"; } //------------------------------------------------------------------------------------------------------------------------------ @@ -229,13 +252,13 @@ namespace tools entry.fee = pd.m_amount_in - pd.m_amount_out; uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known entry.amount = pd.m_amount_in - change - entry.fee; - entry.note = m_wallet.get_tx_note(txid); + entry.note = m_wallet->get_tx_note(txid); for (const auto &d: pd.m_dests) { entry.destinations.push_back(wallet_rpc::transfer_destination()); wallet_rpc::transfer_destination &td = entry.destinations.back(); td.amount = d.amount; - td.address = get_account_address_as_str(m_wallet.testnet(), d.addr); + td.address = get_account_address_as_str(m_wallet->testnet(), d.addr); } entry.type = "out"; @@ -253,7 +276,7 @@ namespace tools entry.timestamp = pd.m_timestamp; entry.fee = pd.m_amount_in - pd.m_amount_out; entry.amount = pd.m_amount_in - pd.m_change - entry.fee; - entry.note = m_wallet.get_tx_note(txid); + entry.note = m_wallet->get_tx_note(txid); entry.type = is_failed ? "failed" : "pending"; } //------------------------------------------------------------------------------------------------------------------------------ @@ -267,18 +290,19 @@ namespace tools entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; entry.fee = 0; // TODO - entry.note = m_wallet.get_tx_note(pd.m_tx_hash); + entry.note = m_wallet->get_tx_note(pd.m_tx_hash); entry.type = "pool"; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - res.balance = m_wallet.balance(); - res.unlocked_balance = m_wallet.unlocked_balance(); + res.balance = m_wallet->balance(); + res.unlocked_balance = m_wallet->unlocked_balance(); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -289,11 +313,12 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - res.address = m_wallet.get_account().get_public_address_str(m_wallet.testnet()); + res.address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -304,11 +329,12 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - res.height = m_wallet.get_blockchain_current_height(); + res.height = m_wallet->get_blockchain_current_height(); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -326,7 +352,7 @@ namespace tools cryptonote::tx_destination_entry de; bool has_payment_id; crypto::hash8 new_payment_id; - if(!get_account_integrated_address_from_str(de.addr, has_payment_id, new_payment_id, m_wallet.testnet(), it->address)) + if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address, false)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address; @@ -397,7 +423,8 @@ namespace tools std::vector<uint8_t> extra; LOG_PRINT_L3("on_transfer_split starts"); - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -413,11 +440,11 @@ namespace tools try { uint64_t mixin = req.mixin; - if (mixin < 2 && m_wallet.use_fork_rules(2, 10)) { + if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) { LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2"); mixin = 2; } - std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); // reject proposed transactions if there are more than one. see on_transfer_split below. if (ptx_vector.size() != 1) @@ -427,7 +454,7 @@ namespace tools return false; } - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); // populate response with tx hash res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx)); @@ -465,7 +492,8 @@ namespace tools std::vector<cryptonote::tx_destination_entry> dsts; std::vector<uint8_t> extra; - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -481,17 +509,17 @@ namespace tools try { uint64_t mixin = req.mixin; - if (mixin < 2 && m_wallet.use_fork_rules(2, 10)) { + if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) { LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2"); mixin = 2; } std::vector<wallet2::pending_tx> ptx_vector; LOG_PRINT_L2("on_transfer_split calling create_transactions_2"); - ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.trusted_daemon); + ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); LOG_PRINT_L2("on_transfer_split called create_transactions_2"); LOG_PRINT_L2("on_transfer_split calling commit_txyy"); - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); LOG_PRINT_L2("on_transfer_split called commit_txyy"); // populate response with tx hashes @@ -530,7 +558,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -539,9 +568,9 @@ namespace tools try { - std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_unmixable_sweep_transactions(req.trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon); - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); // populate response with tx hashes for (auto & ptx : ptx_vector) @@ -582,7 +611,8 @@ namespace tools std::vector<cryptonote::tx_destination_entry> dsts; std::vector<uint8_t> extra; - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -601,9 +631,9 @@ namespace tools try { - std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions_all(dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, req.trusted_daemon); + std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); - m_wallet.commit_tx(ptx_vector); + m_wallet->commit_tx(ptx_vector); // populate response with tx hashes for (auto & ptx : ptx_vector) @@ -641,6 +671,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { crypto::hash8 payment_id; @@ -658,11 +689,11 @@ namespace tools } } - res.integrated_address = m_wallet.get_account().get_public_integrated_address_str(payment_id, m_wallet.testnet()); + res.integrated_address = m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->testnet()); res.payment_id = epee::string_tools::pod_to_hex(payment_id); return true; } - catch (std::exception &e) + catch (const std::exception &e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -673,13 +704,14 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { cryptonote::account_public_address address; crypto::hash8 payment_id; bool has_payment_id; - if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet.testnet(), req.integrated_address)) + if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet->testnet(), req.integrated_address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = "Invalid address"; @@ -691,11 +723,11 @@ namespace tools er.message = "Address is not an integrated address"; return false; } - res.standard_address = get_account_address_as_str(m_wallet.testnet(),address); + res.standard_address = get_account_address_as_str(m_wallet->testnet(),address); res.payment_id = epee::string_tools::pod_to_hex(payment_id); return true; } - catch (std::exception &e) + catch (const std::exception &e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -706,7 +738,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -715,9 +748,9 @@ namespace tools try { - m_wallet.store(); + m_wallet->store(); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -728,6 +761,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); crypto::hash payment_id; crypto::hash8 payment_id8; cryptonote::blobdata payment_id_blob; @@ -757,7 +791,7 @@ namespace tools res.payments.clear(); std::list<wallet2::payment_details> payment_list; - m_wallet.get_payments(payment_id, payment_list); + m_wallet->get_payments(payment_id, payment_list); for (auto & payment : payment_list) { wallet_rpc::payment_details rpc_payment; @@ -775,12 +809,13 @@ namespace tools bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er) { res.payments.clear(); + if (!m_wallet) return not_open(er); /* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */ if (req.payment_ids.empty()) { std::list<std::pair<crypto::hash,wallet2::payment_details>> payment_list; - m_wallet.get_payments(payment_list, req.min_block_height); + m_wallet->get_payments(payment_list, req.min_block_height); for (auto & payment : payment_list) { @@ -829,7 +864,7 @@ namespace tools } std::list<wallet2::payment_details> payment_list; - m_wallet.get_payments(payment_id, payment_list, req.min_block_height); + m_wallet->get_payments(payment_id, payment_list, req.min_block_height); for (auto & payment : payment_list) { @@ -848,6 +883,7 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); if(req.transfer_type.compare("all") != 0 && req.transfer_type.compare("available") != 0 && req.transfer_type.compare("unavailable") != 0) { er.code = WALLET_RPC_ERROR_CODE_TRANSFER_TYPE; @@ -869,7 +905,7 @@ namespace tools } wallet2::transfer_container transfers; - m_wallet.get_transfers(transfers); + m_wallet->get_transfers(transfers); bool transfers_found = false; for (const auto& td : transfers) @@ -896,7 +932,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -905,7 +942,7 @@ namespace tools if (req.key_type.compare("mnemonic") == 0) { - if (!m_wallet.get_seed(res.key)) + if (!m_wallet->get_seed(res.key)) { er.message = "The wallet is non-deterministic. Cannot display seed."; return false; @@ -913,7 +950,7 @@ namespace tools } else if(req.key_type.compare("view_key") == 0) { - res.key = string_tools::pod_to_hex(m_wallet.get_account().get_keys().m_view_secret_key); + res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); } else { @@ -926,7 +963,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -935,9 +973,9 @@ namespace tools try { - m_wallet.rescan_blockchain(); + m_wallet->rescan_blockchain(); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -948,20 +986,22 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; return false; } - res.signature = m_wallet.sign(req.data); + res.signature = m_wallet->sign(req.data); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -971,20 +1011,21 @@ namespace tools cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id; - if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet.testnet(), req.address)) + if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address, false)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = ""; return false; } - res.good = m_wallet.verify(req.data, address, req.signature); + res.good = m_wallet->verify(req.data, address, req.signature); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_stop_wallet(const wallet_rpc::COMMAND_RPC_STOP_WALLET::request& req, wallet_rpc::COMMAND_RPC_STOP_WALLET::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -993,10 +1034,10 @@ namespace tools try { - m_wallet.store(); + m_wallet->store(); m_stop.store(true, std::memory_order_relaxed); } - catch (std::exception& e) + catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -1007,6 +1048,14 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + if (req.txids.size() != req.notes.size()) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; @@ -1019,7 +1068,7 @@ namespace tools while (i != req.txids.end()) { cryptonote::blobdata txid_blob; - if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob)) + if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID; er.message = "TX ID has invalid format"; @@ -1034,7 +1083,7 @@ namespace tools std::list<std::string>::const_iterator in = req.notes.begin(); while (il != txids.end()) { - m_wallet.set_tx_note(*il++, *in++); + m_wallet->set_tx_note(*il++, *in++); } return true; @@ -1043,13 +1092,14 @@ namespace tools bool wallet_rpc_server::on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er) { res.notes.clear(); + if (!m_wallet) return not_open(er); std::list<crypto::hash> txids; std::list<std::string>::const_iterator i = req.txids.begin(); while (i != req.txids.end()) { cryptonote::blobdata txid_blob; - if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob)) + if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID; er.message = "TX ID has invalid format"; @@ -1063,14 +1113,15 @@ namespace tools std::list<crypto::hash>::const_iterator il = txids.begin(); while (il != txids.end()) { - res.notes.push_back(m_wallet.get_tx_note(*il++)); + res.notes.push_back(m_wallet->get_tx_note(*il++)); } return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1087,7 +1138,7 @@ namespace tools if (req.in) { std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet.get_payments(payments, min_height, max_height); + m_wallet->get_payments(payments, min_height, max_height); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.in.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.in.back(), i->second.m_tx_hash, i->first, i->second); @@ -1097,7 +1148,7 @@ namespace tools if (req.out) { std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments; - m_wallet.get_payments_out(payments, min_height, max_height); + m_wallet->get_payments_out(payments, min_height, max_height); for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.out.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.out.back(), i->first, i->second); @@ -1106,7 +1157,7 @@ namespace tools if (req.pending || req.failed) { std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments; - m_wallet.get_unconfirmed_payments_out(upayments); + m_wallet->get_unconfirmed_payments_out(upayments); for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { const tools::wallet2::unconfirmed_transfer_details &pd = i->second; bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed; @@ -1120,10 +1171,10 @@ namespace tools if (req.pool) { - m_wallet.update_pool_state(); + m_wallet->update_pool_state(); std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet.get_unconfirmed_payments(payments); + m_wallet->get_unconfirmed_payments(payments); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.pool.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.pool.back(), i->first, i->second); @@ -1135,7 +1186,8 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er) { - if (m_wallet.restricted()) + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; er.message = "Command unavailable in restricted mode."; @@ -1163,9 +1215,9 @@ namespace tools } std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet.get_payments(payments, 0); + m_wallet->get_payments(payments, 0); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { - if (i->first == txid) + if (i->second.m_tx_hash == txid) { fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second); return true; @@ -1173,7 +1225,7 @@ namespace tools } std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out; - m_wallet.get_payments_out(payments_out, 0); + m_wallet->get_payments_out(payments_out, 0); for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) { if (i->first == txid) { @@ -1183,7 +1235,7 @@ namespace tools } std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments; - m_wallet.get_unconfirmed_payments_out(upayments); + m_wallet->get_unconfirmed_payments_out(upayments); for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { if (i->first == txid) { @@ -1192,10 +1244,10 @@ namespace tools } } - m_wallet.update_pool_state(); + m_wallet->update_pool_state(); std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments; - m_wallet.get_unconfirmed_payments(pool_payments); + m_wallet->get_unconfirmed_payments(pool_payments); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { if (i->second.m_tx_hash == txid) { @@ -1211,9 +1263,10 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); try { - std::vector<std::pair<crypto::key_image, crypto::signature>> ski = m_wallet.export_key_images(); + std::vector<std::pair<crypto::key_image, crypto::signature>> ski = m_wallet->export_key_images(); res.signed_key_images.resize(ski.size()); for (size_t n = 0; n < ski.size(); ++n) { @@ -1234,6 +1287,14 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + try { std::vector<std::pair<crypto::key_image, crypto::signature>> ski; @@ -1242,7 +1303,7 @@ namespace tools { cryptonote::blobdata bd; - if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].key_image, bd)) + if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].key_image, bd) || bd.size() != sizeof(crypto::key_image)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; er.message = "failed to parse key image"; @@ -1250,7 +1311,7 @@ namespace tools } ski[n].first = *reinterpret_cast<const crypto::key_image*>(bd.data()); - if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].signature, bd)) + if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].signature, bd) || bd.size() != sizeof(crypto::signature)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE; er.message = "failed to parse signature"; @@ -1259,7 +1320,7 @@ namespace tools ski[n].second = *reinterpret_cast<const crypto::signature*>(bd.data()); } uint64_t spent = 0, unspent = 0; - uint64_t height = m_wallet.import_key_images(ski, spent, unspent); + uint64_t height = m_wallet->import_key_images(ski, spent, unspent); res.spent = spent; res.unspent = unspent; res.height = height; @@ -1278,7 +1339,7 @@ namespace tools bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er) { std::string error; - std::string uri = m_wallet.make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error); + std::string uri = m_wallet->make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error); if (uri.empty()) { er.code = WALLET_RPC_ERROR_CODE_WRONG_URI; @@ -1292,8 +1353,9 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); std::string error; - if (!m_wallet.parse_uri(req.uri, res.uri.address, res.uri.payment_id, res.uri.amount, res.uri.tx_description, res.uri.recipient_name, res.unknown_parameters, error)) + if (!m_wallet->parse_uri(req.uri, res.uri.address, res.uri.payment_id, res.uri.amount, res.uri.tx_description, res.uri.recipient_name, res.unknown_parameters, error)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_URI; er.message = "Error parsing URI: " + error; @@ -1304,12 +1366,13 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er) { - const auto ab = m_wallet.get_address_book(); + if (!m_wallet) return not_open(er); + const auto ab = m_wallet->get_address_book(); if (req.entries.empty()) { uint64_t idx = 0; for (const auto &entry: ab) - res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet.testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); + res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); } else { @@ -1322,7 +1385,7 @@ namespace tools return false; } const auto &entry = ab[idx]; - res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet.testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); + res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description}); } } return true; @@ -1330,11 +1393,19 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er) { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id8; crypto::hash payment_id = cryptonote::null_hash; - if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id8, m_wallet.testnet(), req.address)) + if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address, false)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address; @@ -1372,26 +1443,34 @@ namespace tools } } } - if (!m_wallet.add_address_book_row(address, payment_id, req.description)) + if (!m_wallet->add_address_book_row(address, payment_id, req.description)) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Failed to add address book entry"; return false; } - res.index = m_wallet.get_address_book().size(); + res.index = m_wallet->get_address_book().size(); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er) { - const auto ab = m_wallet.get_address_book(); + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + + const auto ab = m_wallet->get_address_book(); if (req.index >= ab.size()) { er.code = WALLET_RPC_ERROR_CODE_WRONG_INDEX; er.message = "Index out of range: " + std::to_string(req.index); return false; } - if (!m_wallet.delete_address_book_row(req.index)) + if (!m_wallet->delete_address_book_row(req.index)) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Failed to delete address book entry"; @@ -1400,6 +1479,224 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + try + { + m_wallet->rescan_spent(); + return true; + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = e.what(); + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (!m_trusted_daemon) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "This command requires a trusted daemon."; + return false; + } + + size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2)); + if (req.threads_count < 1 || max_mining_threads_count < req.threads_count) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "The specified number of threads is inappropriate."; + return false; + } + + cryptonote::COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req); + daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + daemon_req.threads_count = req.threads_count; + daemon_req.do_background_mining = req.do_background_mining; + daemon_req.ignore_battery = req.ignore_battery; + + cryptonote::COMMAND_RPC_START_MINING::response daemon_res; + bool r = net_utils::invoke_http_json("/start_mining", daemon_req, daemon_res, m_http_client); + if (!r || daemon_res.status != CORE_RPC_STATUS_OK) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Couldn't start mining due to unknown error."; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er) + { + cryptonote::COMMAND_RPC_STOP_MINING::request daemon_req; + cryptonote::COMMAND_RPC_STOP_MINING::response daemon_res; + bool r = net_utils::invoke_http_json("/stop_mining", daemon_req, daemon_res, m_http_client); + if (!r || daemon_res.status != CORE_RPC_STATUS_OK) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Couldn't stop mining due to unknown error."; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er) + { + crypto::ElectrumWords::get_language_list(res.languages); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er) + { + if (m_wallet_dir.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "No wallet dir configured"; + return false; + } + + namespace po = boost::program_options; + po::variables_map vm2; + const char *ptr = strchr(req.filename.c_str(), '/'); +#ifdef _WIN32 + if (!ptr) + ptr = strchr(req.filename.c_str(), '\\'); + if (!ptr) + ptr = strchr(req.filename.c_str(), ':'); +#endif + if (ptr) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Invalid filename"; + return false; + } + std::string wallet_file = m_wallet_dir + "/" + req.filename; + { + std::vector<std::string> languages; + crypto::ElectrumWords::get_language_list(languages); + std::vector<std::string>::iterator it; + std::string wallet_file; + char *ptr; + + it = std::find(languages.begin(), languages.end(), req.language); + if (it == languages.end()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Unknown language"; + return false; + } + } + { + po::options_description desc("dummy"); + const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"}; + const char *argv[4]; + int argc = 3; + argv[0] = "wallet-rpc"; + argv[1] = "--password"; + argv[2] = req.password.c_str(); + argv[3] = NULL; + vm2 = *m_vm; + command_line::add_arg(desc, arg_password); + po::store(po::parse_command_line(argc, argv, desc), vm2); + } + std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2).first; + if (!wal) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to create wallet"; + return false; + } + wal->set_seed_language(req.language); + cryptonote::COMMAND_RPC_GET_HEIGHT::request hreq; + cryptonote::COMMAND_RPC_GET_HEIGHT::response hres; + hres.height = 0; + bool r = net_utils::invoke_http_json("/getheight", hreq, hres, m_http_client); + wal->set_refresh_from_block_height(hres.height); + crypto::secret_key dummy_key; + try { + wal->generate(wallet_file, req.password, dummy_key, false, false); + } + catch (const std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to generate wallet"; + return false; + } + if (m_wallet) + delete m_wallet; + m_wallet = wal.release(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er) + { + if (m_wallet_dir.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "No wallet dir configured"; + return false; + } + + namespace po = boost::program_options; + po::variables_map vm2; + const char *ptr = strchr(req.filename.c_str(), '/'); +#ifdef _WIN32 + if (!ptr) + ptr = strchr(req.filename.c_str(), '\\'); + if (!ptr) + ptr = strchr(req.filename.c_str(), ':'); +#endif + if (ptr) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Invalid filename"; + return false; + } + std::string wallet_file = m_wallet_dir + "/" + req.filename; + { + po::options_description desc("dummy"); + const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"}; + const char *argv[4]; + int argc = 3; + argv[0] = "wallet-rpc"; + argv[1] = "--password"; + argv[2] = req.password.c_str(); + argv[3] = NULL; + vm2 = *m_vm; + command_line::add_arg(desc, arg_password); + po::store(po::parse_command_line(argc, argv, desc), vm2); + } + std::unique_ptr<tools::wallet2> wal; + try { + wal = tools::wallet2::make_from_file(vm2, wallet_file).first; + } + catch (const std::exception& e) + { + wal = nullptr; + } + if (!wal) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to open wallet"; + return false; + } + if (m_wallet) + delete m_wallet; + m_wallet = wal.release(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ } int main(int argc, char** argv) { @@ -1410,19 +1707,21 @@ int main(int argc, char** argv) { po::options_description desc_params(wallet_args::tr("Wallet options")); tools::wallet2::init_options(desc_params); - command_line::add_arg(desc_params, arg_rpc_bind_ip); command_line::add_arg(desc_params, arg_rpc_bind_port); - command_line::add_arg(desc_params, arg_rpc_login); command_line::add_arg(desc_params, arg_disable_rpc_login); - command_line::add_arg(desc_params, arg_confirm_external_bind); + command_line::add_arg(desc_params, arg_trusted_daemon); + cryptonote::rpc_args::init_options(desc_params); command_line::add_arg(desc_params, arg_wallet_file); command_line::add_arg(desc_params, arg_from_json); + command_line::add_arg(desc_params, arg_wallet_dir); + const auto vm = wallet_args::main( argc, argv, - "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>] [--rpc-bind-port=<port>]", + "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]", desc_params, po::positional_options_description(), + "monero-wallet-rpc.log", true ); if (!vm) @@ -1435,6 +1734,7 @@ int main(int argc, char** argv) { { const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file); const auto from_json = command_line::get_arg(*vm, arg_from_json); + const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir); if(!wallet_file.empty() && !from_json.empty()) { @@ -1442,9 +1742,15 @@ int main(int argc, char** argv) { return 1; } + if (!wallet_dir.empty()) + { + wal = NULL; + goto just_dir; + } + if (wallet_file.empty() && from_json.empty()) { - LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json")); + LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir")); return 1; } @@ -1485,8 +1791,10 @@ int main(int argc, char** argv) { LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what()); return 1; } - tools::wallet_rpc_server wrpc(*wal); - bool r = wrpc.init(*vm); +just_dir: + tools::wallet_rpc_server wrpc; + if (wal) wrpc.set_wallet(wal.release()); + bool r = wrpc.init(&(vm.get())); CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet rpc server")); tools::signal_handler::install([&wrpc, &wal](int) { wrpc.send_stop_signal(); @@ -1497,7 +1805,7 @@ int main(int argc, char** argv) { try { LOG_PRINT_L0(tools::wallet_rpc_server::tr("Storing wallet...")); - wal->store(); + wrpc.stop(); LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stored ok")); } catch (const std::exception& e) diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 98e6e72ed..230dcee5b 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -52,11 +52,14 @@ namespace tools static const char* tr(const char* str); - wallet_rpc_server(wallet2& cr); + wallet_rpc_server(); ~wallet_rpc_server(); - bool init(const boost::program_options::variables_map& vm); + bool init(const boost::program_options::variables_map *vm); bool run(); + void stop(); + void set_wallet(wallet2 *cr); + private: CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map @@ -92,6 +95,12 @@ namespace tools MAP_JON_RPC_WE("get_address_book", on_get_address_book, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY) MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY) MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY) + MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT) + MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING) + MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING) + MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES) + MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET) + MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET) END_JSON_RPC_MAP() END_URI_MAP2() @@ -125,6 +134,12 @@ namespace tools bool on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er); bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er); bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er); + bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er); + bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er); + bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er); + bool on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er); + bool on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er); + bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er); //json rpc v2 bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er); @@ -134,9 +149,14 @@ namespace tools void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd); void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd); void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd); + bool not_open(epee::json_rpc::error& er); - wallet2& m_wallet; + wallet2 *m_wallet; + std::string m_wallet_dir; std::string rpc_login_filename; std::atomic<bool> m_stop; + bool m_trusted_daemon; + epee::net_utils::http::http_simple_client m_http_client; + const boost::program_options::variables_map *m_vm; }; } diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index e132b4e2b..3c10dc41f 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,7 +30,7 @@ #pragma once #include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "crypto/hash.h" #include "wallet_rpc_server_error_codes.h" @@ -119,7 +119,6 @@ namespace wallet_rpc uint64_t unlock_time; std::string payment_id; bool get_tx_key; - bool trusted_daemon; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(destinations) @@ -128,7 +127,6 @@ namespace wallet_rpc KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_key) - KV_SERIALIZE(trusted_daemon) END_KV_SERIALIZE_MAP() }; @@ -158,7 +156,6 @@ namespace wallet_rpc uint64_t unlock_time; std::string payment_id; bool get_tx_keys; - bool trusted_daemon; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(destinations) @@ -167,7 +164,6 @@ namespace wallet_rpc KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_keys) - KV_SERIALIZE(trusted_daemon) END_KV_SERIALIZE_MAP() }; @@ -199,11 +195,9 @@ namespace wallet_rpc struct request { bool get_tx_keys; - bool trusted_daemon; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(get_tx_keys) - KV_SERIALIZE(trusted_daemon) END_KV_SERIALIZE_MAP() }; @@ -240,7 +234,7 @@ namespace wallet_rpc uint64_t unlock_time; std::string payment_id; bool get_tx_keys; - bool trusted_daemon; + uint64_t below_amount; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) @@ -249,7 +243,7 @@ namespace wallet_rpc KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_keys) - KV_SERIALIZE(trusted_daemon) + KV_SERIALIZE(below_amount) END_KV_SERIALIZE_MAP() }; @@ -865,5 +859,113 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_RESCAN_SPENT + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_START_MINING + { + struct request + { + uint64_t threads_count; + bool do_background_mining; + bool ignore_battery; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(threads_count) + KV_SERIALIZE(do_background_mining) + KV_SERIALIZE(ignore_battery) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_STOP_MINING + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_LANGUAGES + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + struct response + { + std::vector<std::string> languages; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(languages) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_CREATE_WALLET + { + struct request + { + std::string filename; + std::string password; + std::string language; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(filename) + KV_SERIALIZE(password) + KV_SERIALIZE(language) + END_KV_SERIALIZE_MAP() + }; + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_OPEN_WALLET + { + struct request + { + std::string filename; + std::string password; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(filename) + KV_SERIALIZE(password) + END_KV_SERIALIZE_MAP() + }; + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; } } diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 7b541c8f6..3c79c0ac3 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -43,3 +43,4 @@ #define WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE -10 #define WALLET_RPC_ERROR_CODE_WRONG_URI -11 #define WALLET_RPC_ERROR_CODE_WRONG_INDEX -12 +#define WALLET_RPC_ERROR_CODE_NOT_OPEN -13 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 60b7a4dba..cb29b27a0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -43,24 +43,30 @@ if (GTest_FOUND) include_directories(SYSTEM ${GTEST_INCLUDE_DIRS}) else () message(STATUS "GTest not found on the system: will use GTest bundled with this source") - add_subdirectory(gtest) - include_directories(SYSTEM "${gtest_SOURCE_DIR}/include" "${gtest_SOURCE_DIR}") - # Emulate the FindGTest module's variable. - set(GTEST_LIBRARIES gtest) - - # Ignore some warnings when building gtest binaries. - if(NOT MSVC) - set_property(TARGET gtest - APPEND_STRING - PROPERTY - COMPILE_FLAGS " -Wno-undef -Wno-sign-compare") - endif() + include(ExternalProject) + ExternalProject_Add(googletest + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/gtest + BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/gtest + DOWNLOAD_COMMAND "" + UPDATE_COMMAND "" + INSTALL_COMMAND "" + ) + add_library(gtest UNKNOWN IMPORTED) + add_library(gtest_main UNKNOWN IMPORTED) + set_target_properties(gtest PROPERTIES + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/gtest/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}" + ) + set_target_properties(gtest_main PROPERTIES + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/gtest/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}" + ) + add_dependencies(gtest googletest) + add_dependencies(gtest_main googletest) - set_property(TARGET gtest - PROPERTY - FOLDER "${folder}") -endif () + # Emulate the FindGTest module's variable. + set(GTEST_LIBRARIES gtest gtest_main) + include_directories(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/gtest/include") +endif (GTest_FOUND) file(COPY data/wallet_9svHk1.keys diff --git a/tests/core_proxy/CMakeLists.txt b/tests/core_proxy/CMakeLists.txt index 63438f21f..d22fecc9c 100644 --- a/tests/core_proxy/CMakeLists.txt +++ b/tests/core_proxy/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index 6edca1050..c12b8e9a7 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index bec9a83bb..106c62f23 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,8 +32,8 @@ #include <boost/program_options/variables_map.hpp> -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/verification_context.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/verification_context.h" #include <unordered_map> namespace tests @@ -91,6 +91,8 @@ namespace tests virtual void on_transaction_relayed(const cryptonote::blobdata& tx) {} bool get_testnet() const { return false; } bool get_pool_transaction(const crypto::hash& id, cryptonote::transaction& tx) const { return false; } - bool get_blocks(uint64_t start_offset, size_t count, std::list<cryptonote::block>& blocks, std::list<cryptonote::transaction>& txs) const { return false; } + bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata, cryptonote::block>>& blocks, std::list<cryptonote::blobdata>& txs) const { return false; } + bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<cryptonote::transaction>& txs, std::list<crypto::hash>& missed_txs) const { return false; } + bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; } }; } diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index c4ef270a9..c1d3161bc 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index 44675996d..9f3939652 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_reward.h b/tests/core_tests/block_reward.h index a5bb69a80..1ec890bbe 100644 --- a/tests/core_tests/block_reward.h +++ b/tests/core_tests/block_reward.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index df8972556..527059141 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index 84f6022c3..d3cf00cc0 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp index 0c4186278..eaaa3e045 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_split_1.h b/tests/core_tests/chain_split_1.h index e563ec273..25fd8d3a3 100644 --- a/tests/core_tests/chain_split_1.h +++ b/tests/core_tests/chain_split_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index c341105e6..a48105f49 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index a8f1fd742..f1d59ca69 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 4cb70e745..b15487a24 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -37,10 +37,10 @@ #include "console_handler.h" #include "p2p/net_node.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/miner.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/miner.h" #include "chaingen.h" diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 04f910efc..eef10312d 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -44,12 +44,12 @@ #include "common/boost_serialization_helper.h" #include "common/command_line.h" -#include "cryptonote_core/account_boost_serialization.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/account_boost_serialization.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_core/cryptonote_boost_serialization.h" +#include "cryptonote_basic/cryptonote_boost_serialization.h" #include "misc_language.h" #undef MONERO_DEFAULT_LOG_CATEGORY diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index f5150f2f7..a01f92229 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -35,8 +35,8 @@ #include "console_handler.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "chaingen.h" #include "chaingen_tests_list.h" diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 5851d9c6a..3e5b949c8 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index 1b9ebd756..26f88dddb 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 69a317ff5..58114b026 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index 936a7897f..b7483e0e5 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index f97d48851..438e39e47 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index 936f29675..75f374434 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/integer_overflow.h b/tests/core_tests/integer_overflow.h index 5a2dc657a..e18c085ea 100644 --- a/tests/core_tests/integer_overflow.h +++ b/tests/core_tests/integer_overflow.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index c29854888..21638354d 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h index f16e665f9..8cd84c462 100644 --- a/tests/core_tests/rct.h +++ b/tests/core_tests/rct.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index c8886eb68..997f3090d 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/ring_signature_1.h b/tests/core_tests/ring_signature_1.h index deabe7c51..1de24d15d 100644 --- a/tests/core_tests/ring_signature_1.h +++ b/tests/core_tests/ring_signature_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index cb585b975..8c0d92a8f 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,9 +29,9 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "include_base_utils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include "misc_language.h" using namespace cryptonote; diff --git a/tests/core_tests/transaction_tests.h b/tests/core_tests/transaction_tests.h index 59586a8a9..b58aae99c 100644 --- a/tests/core_tests/transaction_tests.h +++ b/tests/core_tests/transaction_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index cf018c8e2..885b95c69 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h index da2fe728d..9e49cf0ff 100644 --- a/tests/core_tests/tx_validation.h +++ b/tests/core_tests/tx_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp index 93ddd8a12..f1256cb64 100644 --- a/tests/core_tests/v2_tests.cpp +++ b/tests/core_tests/v2_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/v2_tests.h b/tests/core_tests/v2_tests.h index fbc2b5295..0a2111adf 100644 --- a/tests/core_tests/v2_tests.h +++ b/tests/core_tests/v2_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/CMakeLists.txt b/tests/crypto/CMakeLists.txt index 9c59cd79e..e1c632a00 100644 --- a/tests/crypto/CMakeLists.txt +++ b/tests/crypto/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/tests/crypto/crypto-ops-data.c b/tests/crypto/crypto-ops-data.c index 870544706..dd509ef39 100644 --- a/tests/crypto/crypto-ops-data.c +++ b/tests/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto-ops.c b/tests/crypto/crypto-ops.c index 61de34823..c10f2f750 100644 --- a/tests/crypto/crypto-ops.c +++ b/tests/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto-tests.h b/tests/crypto/crypto-tests.h index 97f2b5130..0271fc22c 100644 --- a/tests/crypto/crypto-tests.h +++ b/tests/crypto/crypto-tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto.cpp b/tests/crypto/crypto.cpp index e6016ece8..6142ebd2b 100644 --- a/tests/crypto/crypto.cpp +++ b/tests/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/hash.c b/tests/crypto/hash.c index f18dcbf26..849f5678e 100644 --- a/tests/crypto/hash.c +++ b/tests/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index 55747991e..4264409b5 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/random.c b/tests/crypto/random.c index 1ff4db01c..4ada988b9 100644 --- a/tests/crypto/random.c +++ b/tests/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/cryptolib.pl b/tests/cryptolib.pl index 3902199fa..8e835ab62 100644 --- a/tests/cryptolib.pl +++ b/tests/cryptolib.pl @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/tests/cryptotest.pl b/tests/cryptotest.pl index 3c72d61fa..cca346da3 100644 --- a/tests/cryptotest.pl +++ b/tests/cryptotest.pl @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/tests/daemon_tests/CMakeLists.txt b/tests/daemon_tests/CMakeLists.txt index 5e2e4ad1e..c6dc69e9d 100644 --- a/tests/daemon_tests/CMakeLists.txt +++ b/tests/daemon_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/tests/daemon_tests/transfers.cpp b/tests/daemon_tests/transfers.cpp index ea851c541..1fbd43fe6 100644 --- a/tests/daemon_tests/transfers.cpp +++ b/tests/daemon_tests/transfers.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,7 +32,7 @@ #include <sstream> #include "wallet/wallet.h" #include "rpc/core_rpc_server.h" -#include "cryptonote_core/account.h" +#include "cryptonote_basic/account.h" #include "net/http_client_abstract_invoke.h" using namespace std; using namespace epee::misc_utils; diff --git a/tests/difficulty/CMakeLists.txt b/tests/difficulty/CMakeLists.txt index 9f1f04fe4..9244c773e 100644 --- a/tests/difficulty/CMakeLists.txt +++ b/tests/difficulty/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index edfce46ab..14f027e4d 100644 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -36,7 +36,7 @@ #include <algorithm> #include "cryptonote_config.h" -#include "cryptonote_core/difficulty.h" +#include "cryptonote_basic/difficulty.h" using namespace std; diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt index 233730955..166476904 100644 --- a/tests/functional_tests/CMakeLists.txt +++ b/tests/functional_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index c0c83c91c..4c9e073d4 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 5666f49bf..48e7b5ab6 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -159,7 +159,7 @@ bool transactions_flow_test(std::string& working_folder, epee::net_utils::http::http_simple_client http_client; COMMAND_RPC_STOP_MINING::request daemon1_req = AUTO_VAL_INIT(daemon1_req); COMMAND_RPC_STOP_MINING::response daemon1_rsp = AUTO_VAL_INIT(daemon1_rsp); - bool r = http_client.set_server(daemon_addr_a) && net_utils::invoke_http_json("/stop_mine", daemon1_req, daemon1_rsp, http_client, std::chrono::seconds(10)); + bool r = http_client.set_server(daemon_addr_a, boost::none) && net_utils::invoke_http_json("/stop_mine", daemon1_req, daemon1_rsp, http_client, std::chrono::seconds(10)); CHECK_AND_ASSERT_MES(r, false, "failed to stop mining"); COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req); diff --git a/tests/functional_tests/transactions_flow_test.h b/tests/functional_tests/transactions_flow_test.h index 4af89fe14..28baf0768 100644 --- a/tests/functional_tests/transactions_flow_test.h +++ b/tests/functional_tests/transactions_flow_test.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_generation_from_blockchain.cpp b/tests/functional_tests/transactions_generation_from_blockchain.cpp index 63ff0343b..b1c2819bf 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.cpp +++ b/tests/functional_tests/transactions_generation_from_blockchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_generation_from_blockchain.h b/tests/functional_tests/transactions_generation_from_blockchain.h index 410cdf33f..1176f0c1d 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.h +++ b/tests/functional_tests/transactions_generation_from_blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/hash-target.cpp b/tests/hash-target.cpp index aeb6688bc..a26ae6059 100644 --- a/tests/hash-target.cpp +++ b/tests/hash-target.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,7 +33,7 @@ #include <cstring> #include <limits> #include "crypto/hash.h" -#include "cryptonote_core/difficulty.h" +#include "cryptonote_basic/difficulty.h" using namespace std; using cryptonote::check_hash; diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt index 7f79ee939..0f579cc6a 100644 --- a/tests/hash/CMakeLists.txt +++ b/tests/hash/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index b65e0cd19..5f8b7856d 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/io.h b/tests/io.h index d2f4e9ff0..e1edfe369 100644 --- a/tests/io.h +++ b/tests/io.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/libwallet_api_tests/CMakeLists.txt b/tests/libwallet_api_tests/CMakeLists.txt index 9be97ec7c..51375440b 100644 --- a/tests/libwallet_api_tests/CMakeLists.txt +++ b/tests/libwallet_api_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -41,8 +41,10 @@ target_link_libraries(libwallet_api_tests PRIVATE wallet epee + ${Boost_CHRONO_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${GTEST_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp index cb1169df3..bf0483b0f 100644 --- a/tests/libwallet_api_tests/main.cpp +++ b/tests/libwallet_api_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,18 +34,19 @@ #include "wallet/wallet2.h" #include "include_base_utils.h" +#include <boost/chrono/chrono.hpp> #include <boost/filesystem.hpp> #include <boost/algorithm/string.hpp> #include <boost/asio.hpp> #include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> #include <iostream> #include <vector> -#include <mutex> -#include <thread> #include <atomic> #include <functional> -#include <condition_variable> using namespace std; @@ -254,15 +255,15 @@ TEST_F(WalletManagerTest, WalletAmountFromString) } -void open_wallet_helper(Monero::WalletManager *wmgr, Monero::Wallet **wallet, const std::string &pass, std::mutex *mutex) +void open_wallet_helper(Monero::WalletManager *wmgr, Monero::Wallet **wallet, const std::string &pass, boost::mutex *mutex) { if (mutex) mutex->lock(); - LOG_PRINT_L3("opening wallet in thread: " << std::this_thread::get_id()); + LOG_PRINT_L3("opening wallet in thread: " << boost::this_thread::get_id()); *wallet = wmgr->openWallet(WALLET_NAME, pass, true); LOG_PRINT_L3("wallet address: " << (*wallet)->address()); LOG_PRINT_L3("wallet status: " << (*wallet)->status()); - LOG_PRINT_L3("closing wallet in thread: " << std::this_thread::get_id()); + LOG_PRINT_L3("closing wallet in thread: " << boost::this_thread::get_id()); if (mutex) mutex->unlock(); } @@ -307,7 +308,7 @@ TEST_F(WalletManagerTest, WalletManagerOpensWalletWithPasswordAndReopen) Monero::Wallet *wallet2 = nullptr; Monero::Wallet *wallet3 = nullptr; - std::mutex mutex; + boost::mutex mutex; open_wallet_helper(wmgr, &wallet2, wrong_wallet_pass, nullptr); ASSERT_TRUE(wallet2 != nullptr); @@ -785,12 +786,12 @@ struct MyWalletListener : public Monero::WalletListener Monero::Wallet * wallet; uint64_t total_tx; uint64_t total_rx; - std::mutex mutex; - std::condition_variable cv_send; - std::condition_variable cv_receive; - std::condition_variable cv_update; - std::condition_variable cv_refresh; - std::condition_variable cv_newblock; + boost::mutex mutex; + boost::condition_variable cv_send; + boost::condition_variable cv_receive; + boost::condition_variable cv_update; + boost::condition_variable cv_refresh; + boost::condition_variable cv_newblock; bool send_triggered; bool receive_triggered; bool newblock_triggered; @@ -880,8 +881,8 @@ TEST_F(WalletTest2, WalletCallBackRefreshedSync) ASSERT_TRUE(wallet_src->init(TESTNET_DAEMON_ADDRESS, 0)); ASSERT_TRUE(wallet_src_listener->refresh_triggered); ASSERT_TRUE(wallet_src->connected()); - std::chrono::seconds wait_for = std::chrono::seconds(60*3); - std::unique_lock<std::mutex> lock (wallet_src_listener->mutex); + boost::chrono::seconds wait_for = boost::chrono::seconds(60*3); + boost::unique_lock<boost::mutex> lock (wallet_src_listener->mutex); wallet_src_listener->cv_refresh.wait_for(lock, wait_for); wmgr->closeWallet(wallet_src); } @@ -895,8 +896,8 @@ TEST_F(WalletTest2, WalletCallBackRefreshedAsync) Monero::Wallet * wallet_src = wmgr->openWallet(CURRENT_SRC_WALLET, TESTNET_WALLET_PASS, true); MyWalletListener * wallet_src_listener = new MyWalletListener(wallet_src); - std::chrono::seconds wait_for = std::chrono::seconds(20); - std::unique_lock<std::mutex> lock (wallet_src_listener->mutex); + boost::chrono::seconds wait_for = boost::chrono::seconds(20); + boost::unique_lock<boost::mutex> lock (wallet_src_listener->mutex); wallet_src->init(MAINNET_DAEMON_ADDRESS, 0); wallet_src->startRefresh(); std::cerr << "TEST: waiting on refresh lock...\n"; @@ -936,8 +937,8 @@ TEST_F(WalletTest2, WalletCallbackSent) ASSERT_TRUE(tx->status() == Monero::PendingTransaction::Status_Ok); ASSERT_TRUE(tx->commit()); - std::chrono::seconds wait_for = std::chrono::seconds(60*3); - std::unique_lock<std::mutex> lock (wallet_src_listener->mutex); + boost::chrono::seconds wait_for = boost::chrono::seconds(60*3); + boost::unique_lock<boost::mutex> lock (wallet_src_listener->mutex); std::cerr << "TEST: waiting on send lock...\n"; wallet_src_listener->cv_send.wait_for(lock, wait_for); std::cerr << "TEST: send lock acquired...\n"; @@ -978,8 +979,8 @@ TEST_F(WalletTest2, WalletCallbackReceived) ASSERT_TRUE(tx->status() == Monero::PendingTransaction::Status_Ok); ASSERT_TRUE(tx->commit()); - std::chrono::seconds wait_for = std::chrono::seconds(60*4); - std::unique_lock<std::mutex> lock (wallet_dst_listener->mutex); + boost::chrono::seconds wait_for = boost::chrono::seconds(60*4); + boost::unique_lock<boost::mutex> lock (wallet_dst_listener->mutex); std::cerr << "TEST: waiting on receive lock...\n"; wallet_dst_listener->cv_receive.wait_for(lock, wait_for); std::cerr << "TEST: receive lock acquired...\n"; @@ -1011,8 +1012,8 @@ TEST_F(WalletTest2, WalletCallbackNewBlock) std::unique_ptr<MyWalletListener> wallet_listener (new MyWalletListener(wallet_src)); // wait max 4 min for new block - std::chrono::seconds wait_for = std::chrono::seconds(60*4); - std::unique_lock<std::mutex> lock (wallet_listener->mutex); + boost::chrono::seconds wait_for = boost::chrono::seconds(60*4); + boost::unique_lock<boost::mutex> lock (wallet_listener->mutex); std::cerr << "TEST: waiting on newblock lock...\n"; wallet_listener->cv_newblock.wait_for(lock, wait_for); std::cerr << "TEST: newblock lock acquired...\n"; @@ -1049,8 +1050,8 @@ TEST_F(WalletManagerMainnetTest, CreateAndRefreshWalletMainNetAsync) Monero::Wallet * wallet = wmgr->createWallet(WALLET_NAME_MAINNET, "", WALLET_LANG); std::unique_ptr<MyWalletListener> wallet_listener (new MyWalletListener(wallet)); - std::chrono::seconds wait_for = std::chrono::seconds(SECONDS_TO_REFRESH); - std::unique_lock<std::mutex> lock (wallet_listener->mutex); + boost::chrono::seconds wait_for = boost::chrono::seconds(SECONDS_TO_REFRESH); + boost::unique_lock<boost::mutex> lock (wallet_listener->mutex); wallet->init(MAINNET_DAEMON_ADDRESS, 0); wallet->startRefresh(); std::cerr << "TEST: waiting on refresh lock...\n"; @@ -1075,8 +1076,8 @@ TEST_F(WalletManagerMainnetTest, OpenAndRefreshWalletMainNetAsync) std::unique_ptr<MyWalletListener> wallet_listener (new MyWalletListener(wallet)); - std::chrono::seconds wait_for = std::chrono::seconds(SECONDS_TO_REFRESH); - std::unique_lock<std::mutex> lock (wallet_listener->mutex); + boost::chrono::seconds wait_for = boost::chrono::seconds(SECONDS_TO_REFRESH); + boost::unique_lock<boost::mutex> lock (wallet_listener->mutex); wallet->init(MAINNET_DAEMON_ADDRESS, 0); wallet->startRefresh(); std::cerr << "TEST: waiting on refresh lock...\n"; @@ -1109,8 +1110,8 @@ TEST_F(WalletManagerMainnetTest, RecoverAndRefreshWalletMainNetAsync) ASSERT_TRUE(wallet->status() == Monero::Wallet::Status_Ok); ASSERT_TRUE(wallet->address() == address); std::unique_ptr<MyWalletListener> wallet_listener (new MyWalletListener(wallet)); - std::chrono::seconds wait_for = std::chrono::seconds(SECONDS_TO_REFRESH); - std::unique_lock<std::mutex> lock (wallet_listener->mutex); + boost::chrono::seconds wait_for = boost::chrono::seconds(SECONDS_TO_REFRESH); + boost::unique_lock<boost::mutex> lock (wallet_listener->mutex); wallet->init(MAINNET_DAEMON_ADDRESS, 0); wallet->startRefresh(); std::cerr << "TEST: waiting on refresh lock...\n"; diff --git a/tests/net_load_tests/CMakeLists.txt b/tests/net_load_tests/CMakeLists.txt index 4db44f25a..78d670c5f 100644 --- a/tests/net_load_tests/CMakeLists.txt +++ b/tests/net_load_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -44,6 +44,7 @@ target_link_libraries(net_load_tests_clt ${Boost_CHRONO_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBARRY} ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index 39e8794b1..376d7ee53 100644 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,7 +32,7 @@ #include <chrono> #include <functional> #include <numeric> -#include <thread> +#include <boost/thread/thread.hpp> #include <vector> #include "gtest/gtest.h" @@ -191,7 +191,7 @@ namespace protected: virtual void SetUp() { - m_thread_count = (std::max)(min_thread_count, std::thread::hardware_concurrency() / 2); + m_thread_count = (std::max)(min_thread_count, boost::thread::hardware_concurrency() / 2); m_tcp_server.get_config_object().m_pcommands_handler = &m_commands_handler; m_tcp_server.get_config_object().m_invoke_timeout = CONNECTION_TIMEOUT; @@ -278,10 +278,10 @@ namespace void parallel_exec(const Func& func) { unit_test::call_counter properly_finished_threads; - std::vector<std::thread> threads(m_thread_count); + std::vector<boost::thread> threads(m_thread_count); for (size_t i = 0; i < threads.size(); ++i) { - threads[i] = std::thread([&, i] { + threads[i] = boost::thread([&, i] { call_func(i, func, 0); properly_finished_threads.inc(); }); diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index 88ef2c7ec..f74282683 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index 3bbbeef7f..e6dee1639 100644 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -28,8 +28,8 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#include <mutex> -#include <thread> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> #include "include_base_utils.h" #include "misc_log_ex.h" @@ -58,7 +58,7 @@ namespace //std::this_thread::sleep_for(std::chrono::milliseconds(10)); - std::unique_lock<std::mutex> lock(m_open_close_test_mutex); + boost::unique_lock<boost::mutex> lock(m_open_close_test_mutex); if (!m_open_close_test_conn_id.is_nil()) { EXIT_ON_ERROR(m_open_close_test_helper->handle_new_connection(context.m_connection_id, true)); @@ -69,7 +69,7 @@ namespace { test_levin_commands_handler::on_connection_close(context); - std::unique_lock<std::mutex> lock(m_open_close_test_mutex); + boost::unique_lock<boost::mutex> lock(m_open_close_test_mutex); if (context.m_connection_id == m_open_close_test_conn_id) { LOG_PRINT_L0("Stop open/close test"); @@ -115,7 +115,7 @@ namespace int handle_start_open_close_test(int command, const CMD_START_OPEN_CLOSE_TEST::request& req, CMD_START_OPEN_CLOSE_TEST::response&, test_connection_context& context) { - std::unique_lock<std::mutex> lock(m_open_close_test_mutex); + boost::unique_lock<boost::mutex> lock(m_open_close_test_mutex); if (0 == m_open_close_test_helper.get()) { LOG_PRINT_L0("Start open/close test (" << req.open_request_target << ", " << req.max_opened_conn_count << ")"); @@ -208,7 +208,7 @@ namespace test_tcp_server& m_tcp_server; boost::uuids::uuid m_open_close_test_conn_id; - std::mutex m_open_close_test_mutex; + boost::mutex m_open_close_test_mutex; std::unique_ptr<open_close_test_helper> m_open_close_test_helper; }; } @@ -218,7 +218,7 @@ int main(int argc, char** argv) //set up logging options mlog_configure(mlog_get_default_log_path("net_load_tests_srv.log"), true); - size_t thread_count = (std::max)(min_thread_count, std::thread::hardware_concurrency() / 2); + size_t thread_count = (std::max)(min_thread_count, boost::thread::hardware_concurrency() / 2); test_tcp_server tcp_server(epee::net_utils::e_connection_type_RPC); if (!tcp_server.init_server(srv_port, "127.0.0.1")) diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index b1050f115..7845515de 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index fe595a4da..1e02bfcaa 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,9 +32,9 @@ #include <vector> -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include "crypto/crypto.h" #include "ringct/rctSigs.h" diff --git a/tests/performance_tests/cn_fast_hash.h b/tests/performance_tests/cn_fast_hash.h new file mode 100644 index 000000000..d02b32676 --- /dev/null +++ b/tests/performance_tests/cn_fast_hash.h @@ -0,0 +1,57 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "crypto/crypto.h" +#include "cryptonote_basic/cryptonote_basic.h" + +template<size_t bytes> +class test_cn_fast_hash +{ +public: + static const size_t loop_count = bytes < 256 ? 100000 : bytes < 4096 ? 10000 : 1000; + + bool init() + { + crypto::rand(bytes, m_data.data()); + return true; + } + + bool test() + { + crypto::hash hash; + crypto::cn_fast_hash(&m_data, bytes, hash); + return true; + } + +private: + std::array<uint8_t, bytes> m_data; +}; diff --git a/tests/performance_tests/cn_slow_hash.h b/tests/performance_tests/cn_slow_hash.h index 5492299cd..7b04d5bcc 100644 --- a/tests/performance_tests/cn_slow_hash.h +++ b/tests/performance_tests/cn_slow_hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,7 +31,7 @@ #pragma once #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" class test_cn_slow_hash { diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index aef455eaa..0610c86ed 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,9 +30,9 @@ #pragma once -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include "multi_tx_test_base.h" diff --git a/tests/performance_tests/derive_public_key.h b/tests/performance_tests/derive_public_key.h index 727dde000..7c9315ebe 100644 --- a/tests/performance_tests/derive_public_key.h +++ b/tests/performance_tests/derive_public_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,7 +31,7 @@ #pragma once #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "single_tx_test_base.h" diff --git a/tests/performance_tests/derive_secret_key.h b/tests/performance_tests/derive_secret_key.h index 94df93843..1a2945a77 100644 --- a/tests/performance_tests/derive_secret_key.h +++ b/tests/performance_tests/derive_secret_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,7 +31,7 @@ #pragma once #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "single_tx_test_base.h" diff --git a/tests/performance_tests/ge_frombytes_vartime.h b/tests/performance_tests/ge_frombytes_vartime.h index c815422f9..491cea097 100644 --- a/tests/performance_tests/ge_frombytes_vartime.h +++ b/tests/performance_tests/ge_frombytes_vartime.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,7 +31,7 @@ #pragma once #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "single_tx_test_base.h" diff --git a/tests/performance_tests/generate_key_derivation.h b/tests/performance_tests/generate_key_derivation.h index 35f41ef89..67a1de3a5 100644 --- a/tests/performance_tests/generate_key_derivation.h +++ b/tests/performance_tests/generate_key_derivation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,7 +31,7 @@ #pragma once #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "single_tx_test_base.h" diff --git a/tests/performance_tests/generate_key_image.h b/tests/performance_tests/generate_key_image.h index fee987969..7d17c2151 100644 --- a/tests/performance_tests/generate_key_image.h +++ b/tests/performance_tests/generate_key_image.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,7 +31,7 @@ #pragma once #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" #include "single_tx_test_base.h" diff --git a/tests/performance_tests/generate_key_image_helper.h b/tests/performance_tests/generate_key_image_helper.h index 44f5c9d12..1332e7f62 100644 --- a/tests/performance_tests/generate_key_image_helper.h +++ b/tests/performance_tests/generate_key_image_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,9 +30,9 @@ #pragma once -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #include "single_tx_test_base.h" diff --git a/tests/performance_tests/generate_keypair.h b/tests/performance_tests/generate_keypair.h index 4ba577e2a..8f0b069da 100644 --- a/tests/performance_tests/generate_keypair.h +++ b/tests/performance_tests/generate_keypair.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -31,7 +31,7 @@ #pragma once #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic.h" class test_generate_keypair { diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h index 7f8645b87..656815db9 100644 --- a/tests/performance_tests/is_out_to_acc.h +++ b/tests/performance_tests/is_out_to_acc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,9 +30,9 @@ #pragma once -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include "single_tx_test_base.h" diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index d08698433..cc9fe86ef 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -43,12 +43,17 @@ #include "generate_key_image_helper.h" #include "generate_keypair.h" #include "is_out_to_acc.h" +#include "sc_reduce32.h" +#include "cn_fast_hash.h" int main(int argc, char** argv) { set_process_affinity(1); set_thread_high_priority(); + mlog_configure(mlog_get_default_log_path("performance_tests.log"), true); + mlog_set_log_level(0); + performance_timer timer; timer.start(); @@ -102,8 +107,11 @@ int main(int argc, char** argv) TEST_PERFORMANCE0(test_derive_secret_key); TEST_PERFORMANCE0(test_ge_frombytes_vartime); TEST_PERFORMANCE0(test_generate_keypair); + TEST_PERFORMANCE0(test_sc_reduce32); TEST_PERFORMANCE0(test_cn_slow_hash); + TEST_PERFORMANCE1(test_cn_fast_hash, 32); + TEST_PERFORMANCE1(test_cn_fast_hash, 16384); std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; diff --git a/tests/performance_tests/multi_tx_test_base.h b/tests/performance_tests/multi_tx_test_base.h index feabab022..92d0d7fd7 100644 --- a/tests/performance_tests/multi_tx_test_base.h +++ b/tests/performance_tests/multi_tx_test_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,9 +32,9 @@ #include <vector> -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include "crypto/crypto.h" template<size_t a_ring_size> diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index 77707148b..a4d49fd82 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -95,10 +95,10 @@ public: int elapsed_time() const { return m_elapsed; } - int time_per_call() const + int time_per_call(int scale = 1) const { static_assert(0 < T::loop_count, "T::loop_count must be greater than 0"); - return m_elapsed / T::loop_count; + return m_elapsed * scale / T::loop_count; } private: @@ -130,7 +130,17 @@ void run_test(const char* test_name) std::cout << test_name << " - OK:\n"; std::cout << " loop count: " << T::loop_count << '\n'; std::cout << " elapsed: " << runner.elapsed_time() << " ms\n"; - std::cout << " time per call: " << runner.time_per_call() << " ms/call\n" << std::endl; + const char *unit = "ms"; + int time_per_call = runner.time_per_call(); + if (time_per_call < 30000) { + time_per_call = runner.time_per_call(1000); +#ifdef _WIN32 + unit = "\xb5s"; +#else + unit = "µs"; +#endif + } + std::cout << " time per call: " << time_per_call << " " << unit << "/call\n" << std::endl; } else { diff --git a/tests/performance_tests/performance_utils.h b/tests/performance_tests/performance_utils.h index 487caa676..061772cbd 100644 --- a/tests/performance_tests/performance_utils.h +++ b/tests/performance_tests/performance_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/sc_reduce32.h b/tests/performance_tests/sc_reduce32.h new file mode 100644 index 000000000..bb3ad1694 --- /dev/null +++ b/tests/performance_tests/sc_reduce32.h @@ -0,0 +1,53 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "crypto/crypto.h" + +class test_sc_reduce32 +{ +public: + static const size_t loop_count = 10000000; + + bool init() + { + m_hash = crypto::rand<crypto::hash>(); + return true; + } + + bool test() + { + crypto::hash reduced = m_hash; + sc_reduce32((unsigned char*)reduced.data); + return true; + } + +private: + crypto::hash m_hash; +}; diff --git a/tests/performance_tests/single_tx_test_base.h b/tests/performance_tests/single_tx_test_base.h index c667b71b7..9e38d979b 100644 --- a/tests/performance_tests/single_tx_test_base.h +++ b/tests/performance_tests/single_tx_test_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,9 +30,9 @@ #pragma once -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_core/cryptonote_tx_utils.h" class single_tx_test_base { diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 08c8213e4..da95a97a9 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # @@ -36,20 +36,22 @@ set(unit_tests_sources chacha8.cpp checkpoints.cpp command_line.cpp + crypto.cpp decompose_amount_into_digits.cpp dns_resolver.cpp epee_boosted_tcp_server.cpp epee_levin_protocol_handler_async.cpp + epee_utils.cpp fee.cpp get_xtype_from_string.cpp - http_auth.cpp + http.cpp main.cpp mnemonics.cpp mul_div.cpp parse_amount.cpp serialization.cpp slow_memmem.cpp - test_format_utils.cpp + test_tx_utils.cpp test_peerlist.cpp test_protocol_pack.cpp thread_group.cpp @@ -58,7 +60,8 @@ set(unit_tests_sources uri.cpp varint.cpp ringct.cpp - output_selection.cpp) + output_selection.cpp + vercmp.cpp) set(unit_tests_headers unit_tests_utils.h) @@ -75,6 +78,8 @@ target_link_libraries(unit_tests wallet p2p epee + ${Boost_CHRONO_LIBRARY} + ${Boost_THREAD_LIBRARY} ${GTEST_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) diff --git a/tests/unit_tests/address_from_url.cpp b/tests/unit_tests/address_from_url.cpp index ad3aca6b4..3630b59d3 100644 --- a/tests/unit_tests/address_from_url.cpp +++ b/tests/unit_tests/address_from_url.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index c6a3a14f0..595085d7d 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -66,7 +66,9 @@ public: virtual void on_transaction_relayed(const cryptonote::blobdata& tx) {} bool get_testnet() const { return false; } bool get_pool_transaction(const crypto::hash& id, cryptonote::transaction& tx) const { return false; } - bool get_blocks(uint64_t start_offset, size_t count, std::list<cryptonote::block>& blocks, std::list<cryptonote::transaction>& txs) const { return false; } + bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata, cryptonote::block>>& blocks, std::list<cryptonote::blobdata>& txs) const { return false; } + bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<cryptonote::transaction>& txs, std::list<crypto::hash>& missed_txs) const { return false; } + bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; } }; typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<test_core>> Server; diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index 53f7f2673..c9d6245cb 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,7 +33,7 @@ #include <cstdint> #include "common/base58.cpp" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "serialization/binary_utils.h" using namespace tools; diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index 5d93c3084..e7d6f2c38 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,7 +30,7 @@ #include "gtest/gtest.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" using namespace cryptonote; diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp index 127a15b44..5592106cb 100644 --- a/tests/unit_tests/blockchain_db.cpp +++ b/tests/unit_tests/blockchain_db.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -40,7 +40,7 @@ #ifdef BERKELEY_DB #include "blockchain_db/berkeleydb/db_bdb.h" #endif -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" using namespace cryptonote; using epee::string_tools::pod_to_hex; @@ -242,27 +242,31 @@ TYPED_TEST_CASE(BlockchainDBTest, implementations); TYPED_TEST(BlockchainDBTest, OpenAndClose) { - std::string fname(tmpnam(NULL)); + boost::filesystem::path tempPath = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + std::string dirPath = tempPath.string(); - this->set_prefix(fname); + this->set_prefix(dirPath); // make sure open does not throw - ASSERT_NO_THROW(this->m_db->open(fname)); + ASSERT_NO_THROW(this->m_db->open(dirPath)); this->get_filenames(); // make sure open when already open DOES throw - ASSERT_THROW(this->m_db->open(fname), DB_OPEN_FAILURE); + ASSERT_THROW(this->m_db->open(dirPath), DB_OPEN_FAILURE); ASSERT_NO_THROW(this->m_db->close()); } TYPED_TEST(BlockchainDBTest, AddBlock) { - std::string fname(tmpnam(NULL)); - this->set_prefix(fname); + + boost::filesystem::path tempPath = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + std::string dirPath = tempPath.string(); + + this->set_prefix(dirPath); // make sure open does not throw - ASSERT_NO_THROW(this->m_db->open(fname)); + ASSERT_NO_THROW(this->m_db->open(dirPath)); this->get_filenames(); this->init_hard_fork(); @@ -288,7 +292,7 @@ TYPED_TEST(BlockchainDBTest, AddBlock) ASSERT_TRUE(compare_blocks(this->m_blocks[0], b)); // assert that we can't add the same block twice - ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]), BLOCK_EXISTS); + ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]), TX_EXISTS); for (auto& h : this->m_blocks[0].tx_hashes) { @@ -302,11 +306,13 @@ TYPED_TEST(BlockchainDBTest, AddBlock) TYPED_TEST(BlockchainDBTest, RetrieveBlockData) { - std::string fname(tmpnam(NULL)); - this->set_prefix(fname); + boost::filesystem::path tempPath = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + std::string dirPath = tempPath.string(); + + this->set_prefix(dirPath); // make sure open does not throw - ASSERT_NO_THROW(this->m_db->open(fname)); + ASSERT_NO_THROW(this->m_db->open(dirPath)); this->get_filenames(); this->init_hard_fork(); diff --git a/tests/unit_tests/canonical_amounts.cpp b/tests/unit_tests/canonical_amounts.cpp index 9bef58d4b..1af817d50 100644 --- a/tests/unit_tests/canonical_amounts.cpp +++ b/tests/unit_tests/canonical_amounts.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include "gtest/gtest.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" static const uint64_t valid_decomposed_outputs[] = { (uint64_t)1, (uint64_t)2, (uint64_t)3, (uint64_t)4, (uint64_t)5, (uint64_t)6, (uint64_t)7, (uint64_t)8, (uint64_t)9, // 1 piconero diff --git a/tests/unit_tests/chacha8.cpp b/tests/unit_tests/chacha8.cpp index 5ec780890..bf0699eba 100644 --- a/tests/unit_tests/chacha8.cpp +++ b/tests/unit_tests/chacha8.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/checkpoints.cpp b/tests/unit_tests/checkpoints.cpp index b1d251d26..d3c4d3b2b 100644 --- a/tests/unit_tests/checkpoints.cpp +++ b/tests/unit_tests/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,7 +30,7 @@ #include "gtest/gtest.h" -#include "cryptonote_core/checkpoints.cpp" +#include "cryptonote_basic/checkpoints.cpp" using namespace cryptonote; diff --git a/tests/unit_tests/command_line.cpp b/tests/unit_tests/command_line.cpp index 4e1c9bff4..408b728b9 100644 --- a/tests/unit_tests/command_line.cpp +++ b/tests/unit_tests/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp new file mode 100644 index 000000000..3a8e787ec --- /dev/null +++ b/tests/unit_tests/crypto.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <cstdint> +#include <gtest/gtest.h> +#include <memory> +#include <sstream> +#include <string> + +#include "cryptonote_basic/cryptonote_basic_impl.h" + +namespace +{ + static constexpr const std::uint8_t source[] = { + 0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea, + 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94, + 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94, + 0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea + }; + + static constexpr const char expected[] = + "8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94" + "6c7251d54154cfa92c173a0dd39c1f948b655970153799af2aeadc9ff1add0ea"; + + template<typename T> + bool is_formatted() + { + T value{}; + + static_assert(alignof(T) == 1, "T must have 1 byte alignment"); + static_assert(sizeof(T) <= sizeof(source), "T is too large for source"); + static_assert(sizeof(T) * 2 <= sizeof(expected), "T is too large for destination"); + std::memcpy(std::addressof(value), source, sizeof(T)); + + std::stringstream out; + out << "BEGIN" << value << "END"; + return out.str() == "BEGIN<" + std::string{expected, sizeof(T) * 2} + ">END"; + } +} + +TEST(Crypto, Ostream) +{ + EXPECT_TRUE(is_formatted<crypto::hash8>()); + EXPECT_TRUE(is_formatted<crypto::hash>()); + EXPECT_TRUE(is_formatted<crypto::public_key>()); + EXPECT_TRUE(is_formatted<crypto::secret_key>()); + EXPECT_TRUE(is_formatted<crypto::signature>()); + EXPECT_TRUE(is_formatted<crypto::key_derivation>()); + EXPECT_TRUE(is_formatted<crypto::key_image>()); +} diff --git a/tests/unit_tests/decompose_amount_into_digits.cpp b/tests/unit_tests/decompose_amount_into_digits.cpp index e40208113..c832510c1 100644 --- a/tests/unit_tests/decompose_amount_into_digits.cpp +++ b/tests/unit_tests/decompose_amount_into_digits.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,7 +33,7 @@ #include <cstdint> #include <vector> -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" #define VEC_FROM_ARR(vec) \ std::vector<uint64_t> vec; \ diff --git a/tests/unit_tests/dns_resolver.cpp b/tests/unit_tests/dns_resolver.cpp index a7ee69c26..aca74a93f 100644 --- a/tests/unit_tests/dns_resolver.cpp +++ b/tests/unit_tests/dns_resolver.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/epee_boosted_tcp_server.cpp b/tests/unit_tests/epee_boosted_tcp_server.cpp index 946db292d..1dfdcb27f 100644 --- a/tests/unit_tests/epee_boosted_tcp_server.cpp +++ b/tests/unit_tests/epee_boosted_tcp_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -28,10 +28,9 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#include <condition_variable> -#include <chrono> -#include <mutex> -#include <thread> +#include <boost/chrono/chrono.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/thread/mutex.hpp> #include "gtest/gtest.h" @@ -88,13 +87,13 @@ TEST(boosted_tcp_server, worker_threads_are_exception_resistant) test_tcp_server srv(epee::net_utils::e_connection_type_RPC); // RPC disables network limit for unit tests ASSERT_TRUE(srv.init_server(test_server_port, test_server_host)); - std::mutex mtx; - std::condition_variable cond; + boost::mutex mtx; + boost::condition_variable cond; int counter = 0; auto counter_incrementer = [&counter, &cond, &mtx]() { - std::unique_lock<std::mutex> lock(mtx); + boost::unique_lock<boost::mutex> lock(mtx); ++counter; if (4 <= counter) { @@ -110,8 +109,8 @@ TEST(boosted_tcp_server, worker_threads_are_exception_resistant) ASSERT_TRUE(srv.async_call([&counter_incrementer]() { counter_incrementer(); throw 4; })); { - std::unique_lock<std::mutex> lock(mtx); - ASSERT_NE(std::cv_status::timeout, cond.wait_for(lock, std::chrono::seconds(5))); + boost::unique_lock<boost::mutex> lock(mtx); + ASSERT_NE(boost::cv_status::timeout, cond.wait_for(lock, boost::chrono::seconds(5))); ASSERT_EQ(4, counter); } @@ -124,8 +123,8 @@ TEST(boosted_tcp_server, worker_threads_are_exception_resistant) ASSERT_TRUE(srv.async_call(counter_incrementer)); { - std::unique_lock<std::mutex> lock(mtx); - ASSERT_NE(std::cv_status::timeout, cond.wait_for(lock, std::chrono::seconds(5))); + boost::unique_lock<boost::mutex> lock(mtx); + ASSERT_NE(boost::cv_status::timeout, cond.wait_for(lock, boost::chrono::seconds(5))); ASSERT_EQ(4, counter); } diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index 2dd3ffe29..d2aa31555 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -28,8 +28,8 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#include <mutex> -#include <thread> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> #include "gtest/gtest.h" @@ -59,7 +59,7 @@ namespace virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, test_levin_connection_context& context) { m_invoke_counter.inc(); - std::unique_lock<std::mutex> lock(m_mutex); + boost::unique_lock<boost::mutex> lock(m_mutex); m_last_command = command; m_last_in_buf = in_buff; buff_out = m_invoke_out_buf; @@ -69,7 +69,7 @@ namespace virtual int notify(int command, const std::string& in_buff, test_levin_connection_context& context) { m_notify_counter.inc(); - std::unique_lock<std::mutex> lock(m_mutex); + boost::unique_lock<boost::mutex> lock(m_mutex); m_last_command = command; m_last_in_buf = in_buff; return m_return_code; @@ -115,7 +115,7 @@ namespace unit_test::call_counter m_new_connection_counter; unit_test::call_counter m_close_connection_counter; - std::mutex m_mutex; + boost::mutex m_mutex; int m_return_code; std::string m_invoke_out_buf; @@ -144,7 +144,7 @@ namespace { //std::cout << "test_connection::do_send()" << std::endl; m_send_counter.inc(); - std::unique_lock<std::mutex> lock(m_mutex); + boost::unique_lock<boost::mutex> lock(m_mutex); m_last_send_data.append(reinterpret_cast<const char*>(ptr), cb); return m_send_return; } @@ -159,7 +159,7 @@ namespace size_t send_counter() const { return m_send_counter.get(); } const std::string& last_send_data() const { return m_last_send_data; } - void reset_last_send_data() { std::unique_lock<std::mutex> lock(m_mutex); m_last_send_data.clear(); } + void reset_last_send_data() { boost::unique_lock<boost::mutex> lock(m_mutex); m_last_send_data.clear(); } bool send_return() const { return m_send_return; } void send_return(bool v) { m_send_return = v; } @@ -172,7 +172,7 @@ namespace test_levin_connection_context m_context; unit_test::call_counter m_send_counter; - std::mutex m_mutex; + boost::mutex m_mutex; std::string m_last_send_data; @@ -305,14 +305,14 @@ TEST_F(positive_test_connection_to_levin_protocol_handler_calls, concurent_handl } }; - const size_t thread_count = std::thread::hardware_concurrency(); - std::vector<std::thread> threads(thread_count); - for (std::thread& th : threads) + const size_t thread_count = boost::thread::hardware_concurrency(); + std::vector<boost::thread> threads(thread_count); + for (boost::thread& th : threads) { - th = std::thread(create_and_destroy_connections); + th = boost::thread(create_and_destroy_connections); } - for (std::thread& th : threads) + for (boost::thread& th : threads) { th.join(); } diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp new file mode 100644 index 000000000..e8ddbe3f5 --- /dev/null +++ b/tests/unit_tests/epee_utils.cpp @@ -0,0 +1,421 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <array> +#include <boost/range/algorithm/equal.hpp> +#include <boost/range/algorithm_ext/iota.hpp> +#include <cstdint> +#include <gtest/gtest.h> +#include <iterator> +#include <string> +#include <sstream> +#include <vector> + +#ifdef _WIN32 +# include <winsock.h> +#else +# include <arpa/inet.h> +#endif + +#include "hex.h" +#include "span.h" +#include "string_tools.h" + +namespace +{ + template<typename Destination, typename Source> + bool can_construct() + { + const unsigned count = + unsigned(std::is_constructible<Destination, Source>()) + + unsigned(std::is_constructible<Destination, Source&>()) + + unsigned(std::is_convertible<Source, Destination>()) + + unsigned(std::is_convertible<Source&, Destination>()) + + unsigned(std::is_assignable<Destination, Source>()) + + unsigned(std::is_assignable<Destination, Source&>()); + EXPECT_TRUE(count == 6 || count == 0) << + "Mismatch on construction results - " << count << " were true"; + return count == 6; + } + + // This is probably stressing the compiler more than the implementation ... + constexpr const epee::span<const char> test_string("a string"); + static_assert(!test_string.empty(), "test failure"); + static_assert(test_string.size() == 9, "test failure"); + static_assert(test_string.size_bytes() == 9, "test_failure"); + static_assert(test_string.begin() == test_string.cbegin(), "test failure"); + static_assert(test_string.end() == test_string.cend(), "test failure"); + static_assert(test_string.cend() - test_string.cbegin() == 9, "test failure"); + static_assert(*test_string.cbegin() == 'a', "test failure"); + static_assert(*(test_string.cend() - 2) == 'g', "test failure"); + static_assert( + epee::span<const char>(test_string).cbegin() + 3 == test_string.cbegin() + 3, + "test failure" + ); + + static_assert(epee::span<char>().empty(), "test failure"); + static_assert(epee::span<char>(nullptr).empty(), "test failure"); + static_assert(epee::span<const char>("foo", 2).size() == 2, "test failure"); + + std::string std_to_hex(const std::vector<unsigned char>& source) + { + std::stringstream out; + out << std::hex; + for (const unsigned char byte : source) + { + out << std::setw(2) << std::setfill('0') << int(byte); + } + return out.str(); + } + + std::vector<unsigned char> get_all_bytes() + { + std::vector<unsigned char> out; + out.resize(256); + boost::range::iota(out, 0); + return out; + } +} + +TEST(Span, Traits) +{ + EXPECT_TRUE((std::is_same<std::size_t, typename epee::span<char>::size_type>())); + EXPECT_TRUE((std::is_same<std::ptrdiff_t, typename epee::span<char>::difference_type>())); + EXPECT_TRUE((std::is_same<char, typename epee::span<char>::value_type>())); + EXPECT_TRUE((std::is_same<char*, typename epee::span<char>::pointer>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<char>::const_pointer>())); + EXPECT_TRUE((std::is_same<char*, typename epee::span<char>::iterator>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<char>::const_iterator>())); + EXPECT_TRUE((std::is_same<char&, typename epee::span<char>::reference>())); + EXPECT_TRUE((std::is_same<const char&, typename epee::span<char>::const_reference>())); + + EXPECT_TRUE((std::is_same<std::size_t, typename epee::span<const char>::size_type>())); + EXPECT_TRUE((std::is_same<std::ptrdiff_t, typename epee::span<const char>::difference_type>())); + EXPECT_TRUE((std::is_same<const char, typename epee::span<const char>::value_type>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<const char>::pointer>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<const char>::const_pointer>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<const char>::iterator>())); + EXPECT_TRUE((std::is_same<const char*, typename epee::span<const char>::const_iterator>())); + EXPECT_TRUE((std::is_same<const char&, typename epee::span<const char>::reference>())); + EXPECT_TRUE((std::is_same<const char&, typename epee::span<const char>::const_reference>())); +} + +TEST(Span, MutableConstruction) +{ + struct no_conversion{}; + + EXPECT_TRUE(std::is_constructible<epee::span<char>>()); + EXPECT_TRUE((std::is_constructible<epee::span<char>, char*, std::size_t>())); + EXPECT_FALSE((std::is_constructible<epee::span<char>, const char*, std::size_t>())); + EXPECT_FALSE((std::is_constructible<epee::span<char>, unsigned char*, std::size_t>())); + + EXPECT_TRUE((can_construct<epee::span<char>, std::nullptr_t>())); + EXPECT_TRUE((can_construct<epee::span<char>, char(&)[1]>())); + + EXPECT_FALSE((can_construct<epee::span<char>, std::vector<char>>())); + EXPECT_FALSE((can_construct<epee::span<char>, std::array<char, 1>>())); + + EXPECT_FALSE((can_construct<epee::span<char>, std::wstring>())); + EXPECT_FALSE((can_construct<epee::span<char>, const std::vector<char>>())); + EXPECT_FALSE((can_construct<epee::span<char>, std::vector<unsigned char>>())); + EXPECT_FALSE((can_construct<epee::span<char>, const std::array<char, 1>>())); + EXPECT_FALSE((can_construct<epee::span<char>, std::array<unsigned char, 1>>())); + EXPECT_FALSE((can_construct<epee::span<char>, const char[1]>())); + EXPECT_FALSE((can_construct<epee::span<char>, unsigned char[1]>())); + EXPECT_FALSE((can_construct<epee::span<char>, epee::span<const char>>())); + EXPECT_FALSE((can_construct<epee::span<char>, epee::span<unsigned char>>())); + EXPECT_FALSE((can_construct<epee::span<char>, no_conversion>())); +} + +TEST(Span, ImmutableConstruction) +{ + struct no_conversion{}; + + EXPECT_TRUE(std::is_constructible<epee::span<const char>>()); + EXPECT_TRUE((std::is_constructible<epee::span<const char>, char*, std::size_t>())); + EXPECT_TRUE((std::is_constructible<epee::span<const char>, const char*, std::size_t>())); + EXPECT_FALSE((std::is_constructible<epee::span<const char>, unsigned char*, std::size_t>())); + + EXPECT_FALSE((can_construct<epee::span<const char>, std::string>())); + EXPECT_FALSE((can_construct<epee::span<const char>, std::vector<char>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, const std::vector<char>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, std::array<char, 1>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, const std::array<char, 1>>())); + + EXPECT_TRUE((can_construct<epee::span<const char>, std::nullptr_t>())); + EXPECT_TRUE((can_construct<epee::span<const char>, char[1]>())); + EXPECT_TRUE((can_construct<epee::span<const char>, const char[1]>())); + EXPECT_TRUE((can_construct<epee::span<const char>, epee::span<const char>>())); + + EXPECT_FALSE((can_construct<epee::span<const char>, std::wstring>())); + EXPECT_FALSE((can_construct<epee::span<const char>, std::vector<unsigned char>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, std::array<unsigned char, 1>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, unsigned char[1]>())); + EXPECT_FALSE((can_construct<epee::span<const char>, epee::span<unsigned char>>())); + EXPECT_FALSE((can_construct<epee::span<const char>, no_conversion>())); +} + +TEST(Span, NoExcept) +{ + EXPECT_TRUE(std::is_nothrow_default_constructible<epee::span<char>>()); + EXPECT_TRUE(std::is_nothrow_move_constructible<epee::span<char>>()); + EXPECT_TRUE(std::is_nothrow_copy_constructible<epee::span<char>>()); + EXPECT_TRUE(std::is_move_assignable<epee::span<char>>()); + EXPECT_TRUE(std::is_copy_assignable<epee::span<char>>()); + + char data[10]; + epee::span<char> lvalue(data); + const epee::span<char> clvalue(data); + EXPECT_TRUE(noexcept(epee::span<char>())); + EXPECT_TRUE(noexcept(epee::span<char>(nullptr))); + EXPECT_TRUE(noexcept(epee::span<char>(nullptr, 0))); + EXPECT_TRUE(noexcept(epee::span<char>(data))); + EXPECT_TRUE(noexcept(epee::span<char>(lvalue))); + EXPECT_TRUE(noexcept(epee::span<char>(clvalue))); + + // conversion from mutable to immutable not yet implemented + // EXPECT_TRUE(noexcept(epee::span<const char>(lvalue))); + // EXPECT_TRUE(noexcept(epee::span<const char>(clvalue))); + + EXPECT_TRUE(noexcept(epee::span<char>(epee::span<char>(lvalue)))); + EXPECT_TRUE(noexcept(lvalue = lvalue)); + EXPECT_TRUE(noexcept(lvalue = clvalue)); + EXPECT_TRUE(noexcept(lvalue = epee::span<char>(lvalue))); +} + +TEST(Span, Nullptr) +{ + const auto check_empty = [](epee::span<const char> data) + { + EXPECT_TRUE(data.empty()); + EXPECT_EQ(data.cbegin(), data.begin()); + EXPECT_EQ(data.cend(), data.end()); + EXPECT_EQ(data.cend(), data.cbegin()); + EXPECT_EQ(0, data.size()); + EXPECT_EQ(0, data.size_bytes()); + }; + check_empty({}); + check_empty(nullptr); +} + +TEST(Span, Writing) +{ + const int expected[] = {-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector<int> source; + + epee::span<int> span; + EXPECT_TRUE(span.empty()); + EXPECT_EQ(0, span.size()); + EXPECT_EQ(0, span.size_bytes()); + + source.resize(15); + span = {source.data(), source.size()}; + EXPECT_FALSE(span.empty()); + EXPECT_EQ(15, span.size()); + EXPECT_EQ(15 * 4, span.size_bytes()); + + boost::range::iota(span, -5); + EXPECT_EQ(span.begin(), span.cbegin()); + EXPECT_EQ(span.end(), span.cend()); + EXPECT_TRUE(boost::range::equal(expected, source)); + EXPECT_TRUE(boost::range::equal(expected, span)); +} + +TEST(Span, ToByteSpan) +{ + const char expected[] = {56, 44, 11, 5}; + EXPECT_TRUE( + boost::range::equal( + std::array<std::uint8_t, 4>{{56, 44, 11, 5}}, + epee::to_byte_span<char>(expected) + ) + ); + EXPECT_TRUE( + boost::range::equal( + std::array<char, 4>{{56, 44, 11, 5}}, + epee::to_byte_span(epee::span<const char>{expected}) + ) + ); +} + +TEST(Span, AsByteSpan) +{ + struct some_pod { char value[4]; }; + const some_pod immutable {{ 5, 10, 12, 127 }}; + EXPECT_TRUE( + boost::range::equal( + std::array<unsigned char, 4>{{5, 10, 12, 127}}, + epee::as_byte_span(immutable) + ) + ); + EXPECT_TRUE( + boost::range::equal( + std::array<std::uint8_t, 3>{{'a', 'y', 0x00}}, epee::as_byte_span("ay") + ) + ); +} + +TEST(ToHex, String) +{ + EXPECT_TRUE(epee::to_hex::string(nullptr).empty()); + EXPECT_EQ( + std::string{"ffab0100"}, + epee::to_hex::string(epee::as_byte_span("\xff\xab\x01")) + ); + + const std::vector<unsigned char> all_bytes = get_all_bytes(); + EXPECT_EQ( + std_to_hex(all_bytes), epee::to_hex::string(epee::to_span(all_bytes)) + ); +} + +TEST(ToHex, Array) +{ + EXPECT_EQ( + (std::array<char, 8>{{'f', 'f', 'a', 'b', '0', '1', '0', '0'}}), + (epee::to_hex::array(std::array<unsigned char, 4>{{0xFF, 0xAB, 0x01, 0x00}})) + ); +} + +TEST(ToHex, Ostream) +{ + std::stringstream out; + epee::to_hex::buffer(out, nullptr); + EXPECT_TRUE(out.str().empty()); + + { + const std::uint8_t source[] = {0xff, 0xab, 0x01, 0x00}; + epee::to_hex::buffer(out, source); + } + + std::string expected{"ffab0100"}; + EXPECT_EQ(expected, out.str()); + + const std::vector<unsigned char> all_bytes = get_all_bytes(); + + expected.append(std_to_hex(all_bytes)); + epee::to_hex::buffer(out, epee::to_span(all_bytes)); + EXPECT_EQ(expected, out.str()); +} + +TEST(ToHex, Formatted) +{ + std::stringstream out; + std::string expected{"<>"}; + + epee::to_hex::formatted(out, nullptr); + EXPECT_EQ(expected, out.str()); + + expected.append("<ffab0100>"); + epee::to_hex::formatted(out, epee::as_byte_span("\xFF\xAB\x01")); + EXPECT_EQ(expected, out.str()); + + const std::vector<unsigned char> all_bytes = get_all_bytes(); + + expected.append("<").append(std_to_hex(all_bytes)).append(">"); + epee::to_hex::formatted(out, epee::to_span(all_bytes)); + EXPECT_EQ(expected, out.str()); +} + +TEST(StringTools, BuffToHex) +{ + const std::vector<unsigned char> all_bytes = get_all_bytes(); + + EXPECT_EQ( + std_to_hex(all_bytes), + (epee::string_tools::buff_to_hex_nodelimer( + std::string{reinterpret_cast<const char*>(all_bytes.data()), all_bytes.size()} + )) + ); +} + +TEST(StringTools, PodToHex) +{ + struct some_pod { unsigned char data[4]; }; + EXPECT_EQ( + std::string{"ffab0100"}, + (epee::string_tools::pod_to_hex(some_pod{{0xFF, 0xAB, 0x01, 0x00}})) + ); +} + +TEST(StringTools, GetIpString) +{ + EXPECT_EQ( + std::string{"0.0.0.0"}, epee::string_tools::get_ip_string_from_int32(0) + ); + EXPECT_EQ( + std::string{"255.0.255.0"}, + epee::string_tools::get_ip_string_from_int32(htonl(0xff00ff00)) + ); + EXPECT_EQ( + std::string{"255.255.255.255"}, + epee::string_tools::get_ip_string_from_int32(htonl(0xffffffff)) + ); +} + +TEST(StringTools, GetIpInt32) +{ + std::uint32_t ip = 0; + EXPECT_FALSE(epee::string_tools::get_ip_int32_from_string(ip, "")); + EXPECT_FALSE(epee::string_tools::get_ip_int32_from_string(ip, "1.")); + EXPECT_FALSE(epee::string_tools::get_ip_int32_from_string(ip, "1.1.")); + EXPECT_FALSE(epee::string_tools::get_ip_int32_from_string(ip, "1.1.1.")); + EXPECT_FALSE(epee::string_tools::get_ip_int32_from_string(ip, "ff.0.ff.0")); + EXPECT_FALSE(epee::string_tools::get_ip_int32_from_string(ip, "1.1.1.256")); + + EXPECT_TRUE(epee::string_tools::get_ip_int32_from_string(ip, "1")); + EXPECT_EQ(htonl(1), ip); + + EXPECT_TRUE(epee::string_tools::get_ip_int32_from_string(ip, "1.1")); + EXPECT_EQ(htonl(0x1000001), ip); + + EXPECT_TRUE(epee::string_tools::get_ip_int32_from_string(ip, "1.1.1")); + EXPECT_EQ(htonl(0x1010001), ip); + + EXPECT_TRUE(epee::string_tools::get_ip_int32_from_string(ip, "0.0.0.0")); + EXPECT_EQ(0, ip); + + EXPECT_TRUE(epee::string_tools::get_ip_int32_from_string(ip, "1.1.1.1")); + EXPECT_EQ(htonl(0x01010101), ip); + +/* + The existing epee conversion function does not work with 255.255.255.255, for + the reasons specified in the inet_addr documentation. Consider fixing in a + future patch. This address is not likely to be used for purposes within + monero. + EXPECT_TRUE(epee::string_tools::get_ip_int32_from_string(ip, "255.255.255.255")); + EXPECT_EQ(htonl(0xffffffff), ip); +*/ + + EXPECT_TRUE(epee::string_tools::get_ip_int32_from_string(ip, "10.0377.0.0377")); + EXPECT_EQ(htonl(0xaff00ff), ip); + + EXPECT_TRUE(epee::string_tools::get_ip_int32_from_string(ip, "0xff.10.0xff.0")); + EXPECT_EQ(htonl(0xff0aff00), ip); +} + diff --git a/tests/unit_tests/fee.cpp b/tests/unit_tests/fee.cpp index a10372c27..3013a8c0c 100644 --- a/tests/unit_tests/fee.cpp +++ b/tests/unit_tests/fee.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -36,6 +36,18 @@ using namespace cryptonote; namespace { + static uint64_t clamp_fee(uint64_t fee) + { + static uint64_t mask = 0; + if (mask == 0) + { + mask = 1; + for (size_t n = PER_KB_FEE_QUANTIZATION_DECIMALS; n < CRYPTONOTE_DISPLAY_DECIMAL_POINT; ++n) + mask *= 10; + } + return (fee + mask - 1) / mask * mask; + } + //-------------------------------------------------------------------------------------------------------------------- class fee : public ::testing::Test { @@ -46,46 +58,46 @@ namespace TEST_F(fee, 10xmr) { // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2), 2000000000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2), 2000000000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100), 2000000000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, 1), 2000000000); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(2000000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(2000000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(2000000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, 1, 3), 2000000000); // higher is inverse proportional - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2), 2000000000 / 2); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10), 2000000000 / 10); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000), 2000000000 / 1000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull), 2000000000 / 20000); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(2000000000 / 2)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(2000000000 / 10)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(2000000000 / 1000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(2000000000 / 20000)); } TEST_F(fee, 1xmr) { // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2), 200000000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2), 200000000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100), 200000000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, 1), 200000000); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(200000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(200000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(200000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, 1, 3), 200000000); // higher is inverse proportional - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2), 200000000 / 2); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10), 200000000 / 10); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000), 200000000 / 1000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull), 200000000 / 20000); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(200000000 / 2)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(200000000 / 10)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(200000000 / 1000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(200000000 / 20000)); } TEST_F(fee, dot3xmr) { // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2), 60000000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2), 60000000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100), 60000000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, 1), 60000000); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(60000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(60000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(60000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, 1, 3), 60000000); // higher is inverse proportional - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2), 60000000 / 2); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10), 60000000 / 10); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000), 60000000 / 1000); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull), 60000000 / 20000); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(60000000 / 2)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(60000000 / 10)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(60000000 / 1000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(60000000 / 20000)); } static bool is_more_or_less(double x, double y) @@ -109,14 +121,15 @@ namespace CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, - CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull + // with clamping, the formula does not hold for such large blocks and small fees + // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull }; for (uint64_t block_reward: block_rewards) { for (uint64_t median_block_size: median_block_sizes) { - ASSERT_TRUE(is_more_or_less(Blockchain::get_dynamic_per_kb_fee(block_reward, median_block_size) * (median_block_size / 1024.) * MAX_MULTIPLIER / (double)block_reward, 1.992 * 1000 / 1024)); + ASSERT_TRUE(is_more_or_less(Blockchain::get_dynamic_per_kb_fee(block_reward, median_block_size, 3) * (median_block_size / 1024.) * MAX_MULTIPLIER / (double)block_reward, 1.992 * 1000 / 1024)); } } } diff --git a/tests/unit_tests/get_xtype_from_string.cpp b/tests/unit_tests/get_xtype_from_string.cpp index eb0d497cc..37156a497 100644 --- a/tests/unit_tests/get_xtype_from_string.cpp +++ b/tests/unit_tests/get_xtype_from_string.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 2682f15d3..08c4276c8 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,7 +32,8 @@ #include "gtest/gtest.h" #include "blockchain_db/lmdb/db_lmdb.h" -#include "cryptonote_core/hardfork.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/hardfork.h" using namespace cryptonote; @@ -59,7 +60,9 @@ public: virtual void block_txn_abort() {} virtual void drop_hard_fork_info() {} virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; } - virtual block get_block(const crypto::hash& h) const { return block(); } + virtual blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); } + virtual blobdata get_block_blob(const crypto::hash& h) const { return blobdata(); } + virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; } virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; } virtual block_header get_block_header(const crypto::hash& h) const { return block_header(); } virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } @@ -89,7 +92,7 @@ public: virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return tx_out_index(); } virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return tx_out_index(); } virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices) const {} - virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) {} + virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) {} virtual bool can_thread_bulk_indices() const { return false; } virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const { return std::vector<uint64_t>(); } virtual std::vector<uint64_t> get_tx_amount_output_indices(const uint64_t tx_index) const { return std::vector<uint64_t>(); } diff --git a/tests/unit_tests/http_auth.cpp b/tests/unit_tests/http.cpp index 97954642f..8d8a0965e 100644 --- a/tests/unit_tests/http_auth.cpp +++ b/tests/unit_tests/http.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -109,7 +109,7 @@ http::http_response_info make_response(const auth_responses& choices) std::string out{" DIGEST "}; write_fields(out, choice); - response.m_additional_fields.push_back( + response.m_header_info.m_etc_fields.push_back( std::make_pair(u8"WWW-authenticate", std::move(out)) ); } @@ -510,7 +510,7 @@ TEST(HTTP_Server_Auth, MD5_sess_auth) TEST(HTTP_Auth, DogFood) { - const auto add_field = [] (http::http_request_info& request, http::http_client_auth& client) + const auto add_auth_field = [] (http::http_request_info& request, http::http_client_auth& client) { auto field = client.get_auth_field(request.m_http_method_str, request.m_URI); EXPECT_TRUE(bool(field)); @@ -529,19 +529,21 @@ TEST(HTTP_Auth, DogFood) request.m_http_method_str = "GET"; request.m_URI = "/FOO"; - const auto response = server.get_response(request); + auto response = server.get_response(request); ASSERT_TRUE(bool(response)); EXPECT_TRUE(is_unauthorized(*response)); + EXPECT_TRUE(response->m_header_info.m_etc_fields.empty()); + response->m_header_info.m_etc_fields = response->m_additional_fields; EXPECT_EQ(http::http_client_auth::kSuccess, client.handle_401(*response)); - EXPECT_TRUE(add_field(request, client)); + EXPECT_TRUE(add_auth_field(request, client)); EXPECT_FALSE(bool(server.get_response(request))); for (unsigned i = 0; i < 1000; ++i) { request.m_http_method_str += std::to_string(i); request.m_header_info.m_etc_fields.clear(); - EXPECT_TRUE(add_field(request, client)); + EXPECT_TRUE(add_auth_field(request, client)); EXPECT_FALSE(bool(server.get_response(request))); } @@ -549,11 +551,13 @@ TEST(HTTP_Auth, DogFood) request.m_header_info.m_etc_fields.clear(); client = http::http_client_auth{user}; EXPECT_EQ(http::http_client_auth::kSuccess, client.handle_401(*response)); - EXPECT_TRUE(add_field(request, client)); + EXPECT_TRUE(add_auth_field(request, client)); - const auto response2 = server.get_response(request); + auto response2 = server.get_response(request); ASSERT_TRUE(bool(response2)); EXPECT_TRUE(is_unauthorized(*response2)); + EXPECT_TRUE(response2->m_header_info.m_etc_fields.empty()); + response2->m_header_info.m_etc_fields = response2->m_additional_fields; const auth_responses parsed1 = parse_response(*response); const auth_responses parsed2 = parse_response(*response2); @@ -564,7 +568,7 @@ TEST(HTTP_Auth, DogFood) // with stale=true client should reset request.m_header_info.m_etc_fields.clear(); EXPECT_EQ(http::http_client_auth::kSuccess, client.handle_401(*response2)); - EXPECT_TRUE(add_field(request, client)); + EXPECT_TRUE(add_auth_field(request, client)); EXPECT_FALSE(bool(server.get_response(request))); // client should give up if stale=false @@ -654,7 +658,7 @@ TEST(HTTP_Client_Auth, MD5) EXPECT_EQ(http::http_client_auth::kBadPassword, auth.handle_401(response)); - response.m_additional_fields.front().second.append(u8"," + write_fields({{u8"stale", u8"TRUE"}})); + response.m_header_info.m_etc_fields.front().second.append(u8"," + write_fields({{u8"stale", u8"TRUE"}})); EXPECT_EQ(http::http_client_auth::kSuccess, auth.handle_401(response)); } @@ -718,7 +722,17 @@ TEST(HTTP_Client_Auth, MD5_auth) } EXPECT_EQ(http::http_client_auth::kBadPassword, auth.handle_401(response)); - response.m_additional_fields.back().second.append(u8"," + write_fields({{u8"stale", u8"trUe"}})); + response.m_header_info.m_etc_fields.back().second.append(u8"," + write_fields({{u8"stale", u8"trUe"}})); EXPECT_EQ(http::http_client_auth::kSuccess, auth.handle_401(response)); } + +TEST(HTTP, Add_Field) +{ + std::string str{"leading text"}; + epee::net_utils::http::add_field(str, "foo", "bar"); + epee::net_utils::http::add_field(str, std::string("bar"), std::string("foo")); + epee::net_utils::http::add_field(str, {"moarbars", "moarfoo"}); + + EXPECT_STREQ("leading textfoo: bar\r\nbar: foo\r\nmoarbars: moarfoo\r\n", str.c_str()); +} diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index e552976a6..b470249a3 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 9c497ef29..e00b87e30 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,10 +34,16 @@ #include <time.h> #include <iostream> #include <boost/algorithm/string.hpp> +#include "mnemonics/chinese_simplified.h" #include "mnemonics/english.h" #include "mnemonics/spanish.h" #include "mnemonics/portuguese.h" #include "mnemonics/japanese.h" +#include "mnemonics/german.h" +#include "mnemonics/italian.h" +#include "mnemonics/russian.h" +#include "mnemonics/french.h" +#include "mnemonics/dutch.h" #include "mnemonics/old_english.h" #include "mnemonics/language_base.h" #include "mnemonics/singleton.h" @@ -133,19 +139,44 @@ namespace } } +TEST(mnemonics, consistency) +{ + try { + std::vector<std::string> language_list; + crypto::ElectrumWords::get_language_list(language_list); + } + catch(const std::exception &e) + { + std::cout << "Error initializing mnemonics: " << e.what() << std::endl; + ASSERT_TRUE(false); + } +} + TEST(mnemonics, all_languages) { srand(time(NULL)); std::vector<Language::Base*> languages({ + Language::Singleton<Language::Chinese_Simplified>::instance(), Language::Singleton<Language::English>::instance(), Language::Singleton<Language::Spanish>::instance(), Language::Singleton<Language::Portuguese>::instance(), Language::Singleton<Language::Japanese>::instance(), + Language::Singleton<Language::German>::instance(), + Language::Singleton<Language::Italian>::instance(), + Language::Singleton<Language::Russian>::instance(), + Language::Singleton<Language::French>::instance(), + Language::Singleton<Language::Dutch>::instance() }); for (std::vector<Language::Base*>::iterator it = languages.begin(); it != languages.end(); it++) { - test_language(*(*it)); + try { + test_language(*(*it)); + } + catch (const std::exception &e) { + std::cout << "Error testing " << (*it)->get_language_name() << " language: " << e.what() << std::endl; + ASSERT_TRUE(false); + } } } diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp index 2a8d032f0..27f3b63a7 100644 --- a/tests/unit_tests/mul_div.cpp +++ b/tests/unit_tests/mul_div.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/output_selection.cpp b/tests/unit_tests/output_selection.cpp index d26f5ae1a..ed436dffd 100644 --- a/tests/unit_tests/output_selection.cpp +++ b/tests/unit_tests/output_selection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/parse_amount.cpp b/tests/unit_tests/parse_amount.cpp index 6c61d63f1..006051a88 100644 --- a/tests/unit_tests/parse_amount.cpp +++ b/tests/unit_tests/parse_amount.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -30,7 +30,7 @@ #include "gtest/gtest.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" using namespace cryptonote; diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index f6409d1ae..af6afa636 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -32,6 +32,7 @@ #include <cstdint> #include <algorithm> +#include <sstream> #include "ringct/rctTypes.h" #include "ringct/rctSigs.h" @@ -1048,3 +1049,13 @@ TEST(ringct, reject_gen_non_simple_ver_simple) rct::rctSig sig = make_sample_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, true); ASSERT_FALSE(rct::verRctSimple(sig)); } + +TEST(ringct, key_ostream) +{ + std::stringstream out; + out << "BEGIN" << rct::H << "END"; + EXPECT_EQ( + std::string{"BEGIN<8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94>END"}, + out.str() + ); +} diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 1083dd4cc..6f9fe7d11 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -35,8 +35,8 @@ #include <vector> #include <boost/foreach.hpp> #include <boost/archive/portable_binary_iarchive.hpp> -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "ringct/rctSigs.h" #include "serialization/serialization.h" #include "serialization/binary_archive.h" @@ -331,6 +331,7 @@ TEST(Serialization, serializes_transacion_signatures_correctly) // Miner tx with empty signatures 2nd vector tx.signatures.resize(1); + tx.invalidate_hashes(); ASSERT_TRUE(serialization::dump_binary(tx, blob)); ASSERT_EQ(7, blob.size()); // 5 bytes + 2 bytes vin[0] + 0 bytes extra + 0 bytes signatures ASSERT_TRUE(serialization::parse_binary(blob, tx1)); @@ -345,16 +346,19 @@ TEST(Serialization, serializes_transacion_signatures_correctly) tx.signatures.resize(2); tx.signatures[0].resize(0); tx.signatures[1].resize(0); + tx.invalidate_hashes(); ASSERT_FALSE(serialization::dump_binary(tx, blob)); // Miner tx with 2 signatures tx.signatures[0].resize(1); tx.signatures[1].resize(1); + tx.invalidate_hashes(); ASSERT_FALSE(serialization::dump_binary(tx, blob)); // Two txin_gen, no signatures tx.vin.push_back(txin_gen1); tx.signatures.resize(0); + tx.invalidate_hashes(); ASSERT_TRUE(serialization::dump_binary(tx, blob)); ASSERT_EQ(9, blob.size()); // 5 bytes + 2 * 2 bytes vins + 0 bytes extra + 0 bytes signatures ASSERT_TRUE(serialization::parse_binary(blob, tx1)); @@ -363,10 +367,12 @@ TEST(Serialization, serializes_transacion_signatures_correctly) // Two txin_gen, signatures vector contains only one empty element tx.signatures.resize(1); + tx.invalidate_hashes(); ASSERT_FALSE(serialization::dump_binary(tx, blob)); // Two txin_gen, signatures vector contains two empty elements tx.signatures.resize(2); + tx.invalidate_hashes(); ASSERT_TRUE(serialization::dump_binary(tx, blob)); ASSERT_EQ(9, blob.size()); // 5 bytes + 2 * 2 bytes vins + 0 bytes extra + 0 bytes signatures ASSERT_TRUE(serialization::parse_binary(blob, tx1)); @@ -375,18 +381,21 @@ TEST(Serialization, serializes_transacion_signatures_correctly) // Two txin_gen, signatures vector contains three empty elements tx.signatures.resize(3); + tx.invalidate_hashes(); ASSERT_FALSE(serialization::dump_binary(tx, blob)); // Two txin_gen, signatures vector contains two non empty elements tx.signatures.resize(2); tx.signatures[0].resize(1); tx.signatures[1].resize(1); + tx.invalidate_hashes(); ASSERT_FALSE(serialization::dump_binary(tx, blob)); // A few bytes instead of signature tx.vin.clear(); tx.vin.push_back(txin_gen1); tx.signatures.clear(); + tx.invalidate_hashes(); ASSERT_TRUE(serialization::dump_binary(tx, blob)); blob.append(std::string(sizeof(crypto::signature) / 2, 'x')); ASSERT_FALSE(serialization::parse_binary(blob, tx1)); @@ -406,6 +415,7 @@ TEST(Serialization, serializes_transacion_signatures_correctly) tx.vin.push_back(txin_to_key1); tx.signatures.resize(1); tx.signatures[0].resize(2); + tx.invalidate_hashes(); ASSERT_FALSE(serialization::dump_binary(tx, blob)); // Too much signatures for two inputs @@ -413,24 +423,28 @@ TEST(Serialization, serializes_transacion_signatures_correctly) tx.signatures[0].resize(2); tx.signatures[1].resize(2); tx.signatures[2].resize(2); + tx.invalidate_hashes(); ASSERT_FALSE(serialization::dump_binary(tx, blob)); // First signatures vector contains too little elements tx.signatures.resize(2); tx.signatures[0].resize(1); tx.signatures[1].resize(2); + tx.invalidate_hashes(); ASSERT_FALSE(serialization::dump_binary(tx, blob)); // First signatures vector contains too much elements tx.signatures.resize(2); tx.signatures[0].resize(3); tx.signatures[1].resize(2); + tx.invalidate_hashes(); ASSERT_FALSE(serialization::dump_binary(tx, blob)); // There are signatures for each input tx.signatures.resize(2); tx.signatures[0].resize(2); tx.signatures[1].resize(2); + tx.invalidate_hashes(); ASSERT_TRUE(serialization::dump_binary(tx, blob)); ASSERT_TRUE(serialization::parse_binary(blob, tx1)); ASSERT_EQ(tx, tx1); @@ -986,8 +1000,6 @@ TEST(Serialization, portability_unsigned_tx) ASSERT_TRUE(tcd.selected_transfers.front() == 2); // tcd.extra ASSERT_TRUE(tcd.extra.size() == 68); - string tcd_extra_str = epee::string_tools::buff_to_hex(string(reinterpret_cast<char*>(tcd.extra.data()), tcd.extra.size())); - ASSERT_TRUE(tcd_extra_str == "0x2 0x21 0x0 0xf8 0xd 0xbc 0xfc 0xa2 0x2d 0x84 0x1e 0xa0 0x46 0x18 0x7a 0x5b 0x19 0xea 0x4d 0xd1 0xa2 0x8a 0x58 0xa8 0x72 0x9 0xd5 0xdf 0x2 0x30 0x60 0xac 0x9e 0x48 0x84 0x1 0xb2 0xfd 0x5d 0x4e 0x45 0x8b 0xf1 0x28 0xa0 0xc8 0x30 0xd1 0x35 0x4f 0x47 0xb9 0xed 0xc9 0x82 0x8c 0x83 0x37 0x7d 0xb6 0xb5 0xe5 0x3d 0xff 0x64 0xb0 0xde 0x7f "); // tcd.{unlock_time, use_rct} ASSERT_TRUE(tcd.unlock_time == 0); ASSERT_TRUE(tcd.use_rct); @@ -1143,8 +1155,6 @@ TEST(Serialization, portability_signed_tx) ASSERT_TRUE(tcd.selected_transfers.front() == 2); // ptx.construction_data.extra ASSERT_TRUE(tcd.extra.size() == 68); - string tcd_extra_str = epee::string_tools::buff_to_hex(string(reinterpret_cast<char*>(tcd.extra.data()), tcd.extra.size())); - ASSERT_TRUE(tcd_extra_str == "0x2 0x21 0x0 0xf8 0xd 0xbc 0xfc 0xa2 0x2d 0x84 0x1e 0xa0 0x46 0x18 0x7a 0x5b 0x19 0xea 0x4d 0xd1 0xa2 0x8a 0x58 0xa8 0x72 0x9 0xd5 0xdf 0x2 0x30 0x60 0xac 0x9e 0x48 0x84 0x1 0xb2 0xfd 0x5d 0x4e 0x45 0x8b 0xf1 0x28 0xa0 0xc8 0x30 0xd1 0x35 0x4f 0x47 0xb9 0xed 0xc9 0x82 0x8c 0x83 0x37 0x7d 0xb6 0xb5 0xe5 0x3d 0xff 0x64 0xb0 0xde 0x7f "); // ptx.construction_data.{unlock_time, use_rct} ASSERT_TRUE(tcd.unlock_time == 0); ASSERT_TRUE(tcd.use_rct); diff --git a/tests/unit_tests/slow_memmem.cpp b/tests/unit_tests/slow_memmem.cpp index d36b9f532..0312019be 100644 --- a/tests/unit_tests/slow_memmem.cpp +++ b/tests/unit_tests/slow_memmem.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index 60a6cdd31..df56f38eb 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp index a9a1a34a6..83352d503 100644 --- a/tests/unit_tests/test_protocol_pack.cpp +++ b/tests/unit_tests/test_protocol_pack.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/test_format_utils.cpp b/tests/unit_tests/test_tx_utils.cpp index 463d62529..0ff91c247 100644 --- a/tests/unit_tests/test_format_utils.cpp +++ b/tests/unit_tests/test_tx_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -33,7 +33,7 @@ #include <vector> #include "common/util.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/cryptonote_tx_utils.h" namespace { diff --git a/tests/unit_tests/thread_group.cpp b/tests/unit_tests/thread_group.cpp index 2e7a78353..c0ba3d38b 100644 --- a/tests/unit_tests/thread_group.cpp +++ b/tests/unit_tests/thread_group.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/unit_tests_utils.h b/tests/unit_tests/unit_tests_utils.h index 4b8fc0877..11230c0db 100644 --- a/tests/unit_tests/unit_tests_utils.h +++ b/tests/unit_tests/unit_tests_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/varint.cpp b/tests/unit_tests/varint.cpp index a483cbd5f..2b31cdfdf 100644 --- a/tests/unit_tests/varint.cpp +++ b/tests/unit_tests/varint.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016, The Monero Project +// Copyright (c) 2014-2017, The Monero Project // // All rights reserved. // @@ -34,8 +34,8 @@ #include <iostream> #include <vector> #include <boost/foreach.hpp> -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" #include "serialization/serialization.h" #include "serialization/binary_archive.h" #include "serialization/json_archive.h" diff --git a/tests/unit_tests/vercmp.cpp b/tests/unit_tests/vercmp.cpp new file mode 100644 index 000000000..d48dfdf7c --- /dev/null +++ b/tests/unit_tests/vercmp.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +#include "common/util.h" + +TEST(vercmp, empty) { ASSERT_TRUE(tools::vercmp("", "") == 0); } +TEST(vercmp, empty0) { ASSERT_TRUE(tools::vercmp("", "0") == 0); } +TEST(vercmp, empty1) { ASSERT_TRUE(tools::vercmp("0", "") == 0); } +TEST(vercmp, zero_zero) { ASSERT_TRUE(tools::vercmp("0", "0") == 0); } +TEST(vercmp, one_one) { ASSERT_TRUE(tools::vercmp("1", "1") == 0); } +TEST(vercmp, one_two) { ASSERT_TRUE(tools::vercmp("1", "2") < 0); } +TEST(vercmp, two_one) { ASSERT_TRUE(tools::vercmp("2", "1") > 0); } +TEST(vercmp, ten_nine) { ASSERT_TRUE(tools::vercmp("10", "9") > 0); } +TEST(vercmp, one_dot_ten_one_dot_nine) { ASSERT_TRUE(tools::vercmp("1.10", "1.9") > 0); } +TEST(vercmp, one_one_dot_nine) { ASSERT_TRUE(tools::vercmp("1", "1.9") < 0); } + diff --git a/translations/monero.ts b/translations/monero.ts index 3a366f5fe..1862e88ff 100644 --- a/translations/monero.ts +++ b/translations/monero.ts @@ -1,1249 +1,1606 @@ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> -<TS version="2.0"> +<TS version="2.1"> +<context> + <name>Monero::AddressBookImpl</name> + <message> + <location filename="../src/wallet/api/address_book.cpp" line="55"/> + <source>Invalid destination address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/address_book.cpp" line="65"/> + <source>Invalid payment ID. Short payment ID should only be used in an integrated address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/address_book.cpp" line="72"/> + <source>Invalid payment ID</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/address_book.cpp" line="79"/> + <source>Integrated address and long payment id can't be used at the same time</source> + <translation type="unfinished"></translation> + </message> +</context> <context> <name>Monero::PendingTransactionImpl</name> <message> - <location filename="../src/wallet/api/pending_transaction.cpp" line="95"/> + <location filename="../src/wallet/api/pending_transaction.cpp" line="90"/> + <source>Attempting to save transaction to file, but specified file(s) exist. Exiting to not risk overwriting. File:</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/pending_transaction.cpp" line="97"/> + <source>Failed to write transaction(s) to file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/pending_transaction.cpp" line="114"/> <source>daemon is busy. Please try again later.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/pending_transaction.cpp" line="98"/> + <location filename="../src/wallet/api/pending_transaction.cpp" line="117"/> <source>no connection to daemon. Please make sure daemon is running.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/pending_transaction.cpp" line="102"/> + <location filename="../src/wallet/api/pending_transaction.cpp" line="121"/> <source>transaction %s was rejected by daemon with status: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/pending_transaction.cpp" line="105"/> + <location filename="../src/wallet/api/pending_transaction.cpp" line="126"/> + <source>. Reason: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/pending_transaction.cpp" line="128"/> <source>Unknown exception: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/pending_transaction.cpp" line="108"/> + <location filename="../src/wallet/api/pending_transaction.cpp" line="131"/> <source>Unhandled exception</source> <translation type="unfinished"></translation> </message> </context> <context> + <name>Monero::UnsignedTransactionImpl</name> + <message> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="75"/> + <source>This is a watch only wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="85"/> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="92"/> + <source>Failed to sign transaction</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="134"/> + <source>Claimed change does not go to a paid address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="140"/> + <source>Claimed change is larger than payment to the change address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="146"/> + <source>Change does to more than one address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="158"/> + <source>sending %s to %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="164"/> + <source>with no destinations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="170"/> + <source>%s change to %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="173"/> + <source>no change</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/unsigned_transaction.cpp" line="175"/> + <source>Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. %s</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>Monero::WalletImpl</name> <message> - <location filename="../src/wallet/api/wallet.cpp" line="609"/> + <location filename="../src/wallet/api/wallet.cpp" line="926"/> <source>payment id has invalid format, expected 16 or 64 character hex string: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="619"/> + <location filename="../src/wallet/api/wallet.cpp" line="936"/> <source>Failed to add short payment id: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="645"/> - <location filename="../src/wallet/api/wallet.cpp" line="739"/> + <location filename="../src/wallet/api/wallet.cpp" line="962"/> + <location filename="../src/wallet/api/wallet.cpp" line="1056"/> <source>daemon is busy. Please try again later.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="648"/> - <location filename="../src/wallet/api/wallet.cpp" line="742"/> + <location filename="../src/wallet/api/wallet.cpp" line="965"/> + <location filename="../src/wallet/api/wallet.cpp" line="1059"/> <source>no connection to daemon. Please make sure daemon is running.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="651"/> - <location filename="../src/wallet/api/wallet.cpp" line="745"/> + <location filename="../src/wallet/api/wallet.cpp" line="968"/> + <location filename="../src/wallet/api/wallet.cpp" line="1062"/> <source>RPC error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="654"/> - <location filename="../src/wallet/api/wallet.cpp" line="748"/> + <location filename="../src/wallet/api/wallet.cpp" line="1065"/> <source>failed to get random outputs to mix</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="661"/> - <location filename="../src/wallet/api/wallet.cpp" line="755"/> + <location filename="../src/wallet/api/wallet.cpp" line="978"/> + <location filename="../src/wallet/api/wallet.cpp" line="1072"/> <source>not enough money to transfer, available only %s, sent amount %s</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="670"/> - <location filename="../src/wallet/api/wallet.cpp" line="764"/> + <location filename="../src/wallet/api/wallet.cpp" line="403"/> + <source>failed to parse address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="415"/> + <source>failed to parse secret spend key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="425"/> + <source>No view key supplied, cancelled</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="432"/> + <source>failed to parse secret view key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="442"/> + <source>failed to verify secret spend key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="447"/> + <source>spend key does not match address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="453"/> + <source>failed to verify secret view key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="458"/> + <source>view key does not match address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="476"/> + <source>failed to generate new wallet: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="783"/> + <source>Failed to load unsigned transactions</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="804"/> + <source>Failed to load transaction from file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="822"/> + <source>Wallet is view only</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="831"/> + <source>failed to save file </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="858"/> + <source>Failed to import key images: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="971"/> + <source>failed to get random outputs to mix: %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="987"/> + <location filename="../src/wallet/api/wallet.cpp" line="1081"/> <source>not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="679"/> - <location filename="../src/wallet/api/wallet.cpp" line="773"/> + <location filename="../src/wallet/api/wallet.cpp" line="996"/> + <location filename="../src/wallet/api/wallet.cpp" line="1090"/> <source>not enough outputs for specified mixin_count</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="681"/> - <location filename="../src/wallet/api/wallet.cpp" line="775"/> + <location filename="../src/wallet/api/wallet.cpp" line="998"/> + <location filename="../src/wallet/api/wallet.cpp" line="1092"/> <source>output amount</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="681"/> - <location filename="../src/wallet/api/wallet.cpp" line="775"/> + <location filename="../src/wallet/api/wallet.cpp" line="998"/> + <location filename="../src/wallet/api/wallet.cpp" line="1092"/> <source>found outputs to mix</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="686"/> - <location filename="../src/wallet/api/wallet.cpp" line="780"/> + <location filename="../src/wallet/api/wallet.cpp" line="1003"/> + <location filename="../src/wallet/api/wallet.cpp" line="1097"/> <source>transaction was not constructed</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="690"/> - <location filename="../src/wallet/api/wallet.cpp" line="784"/> + <location filename="../src/wallet/api/wallet.cpp" line="1007"/> + <location filename="../src/wallet/api/wallet.cpp" line="1101"/> <source>transaction %s was rejected by daemon with status: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="697"/> - <location filename="../src/wallet/api/wallet.cpp" line="791"/> + <location filename="../src/wallet/api/wallet.cpp" line="1014"/> + <location filename="../src/wallet/api/wallet.cpp" line="1108"/> <source>one of destinations is zero</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="700"/> - <location filename="../src/wallet/api/wallet.cpp" line="794"/> + <location filename="../src/wallet/api/wallet.cpp" line="1017"/> + <location filename="../src/wallet/api/wallet.cpp" line="1111"/> <source>failed to find a suitable way to split transactions</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="703"/> - <location filename="../src/wallet/api/wallet.cpp" line="797"/> + <location filename="../src/wallet/api/wallet.cpp" line="1020"/> + <location filename="../src/wallet/api/wallet.cpp" line="1114"/> <source>unknown transfer error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="706"/> - <location filename="../src/wallet/api/wallet.cpp" line="800"/> + <location filename="../src/wallet/api/wallet.cpp" line="1023"/> + <location filename="../src/wallet/api/wallet.cpp" line="1117"/> <source>internal error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="709"/> - <location filename="../src/wallet/api/wallet.cpp" line="803"/> + <location filename="../src/wallet/api/wallet.cpp" line="1026"/> + <location filename="../src/wallet/api/wallet.cpp" line="1120"/> <source>unexpected error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet.cpp" line="712"/> - <location filename="../src/wallet/api/wallet.cpp" line="806"/> + <location filename="../src/wallet/api/wallet.cpp" line="1029"/> + <location filename="../src/wallet/api/wallet.cpp" line="1123"/> <source>unknown error</source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="1403"/> + <source>Rescan spent can only be used with a trusted daemon</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>Monero::WalletManagerImpl</name> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="161"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="192"/> <source>failed to parse txid</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="168"/> - <location filename="../src/wallet/api/wallet_manager.cpp" line="175"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="199"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="206"/> <source>failed to parse tx key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="186"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="217"/> <source>failed to parse address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="197"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="227"/> <source>failed to get transaction from daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="208"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="238"/> <source>failed to parse transaction from daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="215"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="245"/> <source>failed to validate transaction from daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="220"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="250"/> <source>failed to get the right transaction from daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="227"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="257"/> <source>failed to generate key derivation from supplied parameters</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="283"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="313"/> <source>error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="289"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="319"/> <source>received</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="289"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="319"/> <source>in txid</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/api/wallet_manager.cpp" line="293"/> + <location filename="../src/wallet/api/wallet_manager.cpp" line="323"/> <source>received nothing in txid</source> <translation type="unfinished"></translation> </message> </context> <context> + <name>Wallet</name> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="212"/> + <source>Failed to parse address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="219"/> + <source>Failed to parse key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="227"/> + <source>failed to verify key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/api/wallet.cpp" line="237"/> + <source>key does not match address</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>command_line</name> <message> - <location filename="../src/common/command_line.cpp" line="67"/> + <location filename="../src/common/command_line.cpp" line="69"/> <source>yes</source> <translation type="unfinished"></translation> </message> </context> <context> + <name>cryptonote::rpc_args</name> + <message> + <location filename="../src/rpc/rpc_args.cpp" line="38"/> + <source>Specify ip to bind rpc server</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/rpc/rpc_args.cpp" line="39"/> + <source>Specify username[:password] required for RPC server</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/rpc/rpc_args.cpp" line="40"/> + <source>Confirm rpc-bind-ip value is NOT a loopback (local) IP</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/rpc/rpc_args.cpp" line="66"/> + <source>Invalid IP address given for --</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/rpc/rpc_args.cpp" line="74"/> + <source> permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/rpc/rpc_args.cpp" line="89"/> + <source>Username specified with --</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/rpc/rpc_args.cpp" line="89"/> + <source> cannot be empty</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>cryptonote::simple_wallet</name> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="276"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="280"/> <source>Commands: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="354"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="381"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="411"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="457"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="519"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="555"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="592"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="622"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1495"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1502"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1659"/> <source>failed to read wallet password</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="362"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="389"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="419"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="465"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="527"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="563"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="600"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="630"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1417"/> <source>invalid password</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="653"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="680"/> <source>start_mining [<number_of_threads>] - Start mining in daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="654"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="681"/> <source>Stop mining in daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="655"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="682"/> <source>Save current blockchain data</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="657"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="684"/> <source>Show current wallet balance</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="660"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="687"/> <source>Show blockchain height</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="669"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="697"/> <source>Show current wallet public address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="691"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="723"/> <source>Show this help</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="714"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="752"/> <source>set seed: needs an argument. available options: language</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="729"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="765"/> <source>set always-confirm-transfers: needs an argument (0 or 1)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="832"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="932"/> <source>set: unrecognized argument(s)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="847"/> - <source>wrong number format, use: set_log <log_level_number_0-4></source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="852"/> - <source>wrong number range, use: set_log <log_level_number_0-4></source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1378"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1544"/> <source>wallet file path not valid: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="894"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="984"/> <source>Attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="943"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1033"/> <source>PLEASE NOTE: the following 25 words can be used to recover access to your wallet. Please write them down and store them somewhere safe and secure. Please do not store them in your email or on file storage services outside of your immediate control. </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="986"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1076"/> <source>specify a recovery parameter with the --electrum-seed="words list here"</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1200"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1353"/> <source>wallet failed to connect to daemon: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1208"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1361"/> <source>Daemon uses a different RPC major version (%u) than the wallet (%u): %s. Either update one of them, or use --allow-mismatched-daemon-version.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1227"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1380"/> <source>List of available languages for your wallet's seed:</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1236"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1389"/> <source>Enter the number corresponding to the language of your choice: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1280"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1446"/> <source>You had been using a deprecated version of the wallet. Please use the new seed that we provide. </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1304"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1361"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1470"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1527"/> <source>Generated new wallet: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1310"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1366"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1476"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1532"/> <source>failed to generate new wallet: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1393"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1559"/> <source>Opened watch-only wallet</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1393"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1559"/> <source>Opened wallet</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1402"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1568"/> <source>You had been using a deprecated version of the wallet. Please proceed to upgrade your wallet. </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1417"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1583"/> <source>You had been using a deprecated version of the wallet. Your wallet file format is being upgraded now. </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1425"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1591"/> <source>failed to load wallet: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1433"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1599"/> <source>Use "help" command to see the list of available commands. </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1477"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1643"/> <source>Wallet data saved</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1492"/> - <source>Password for the new watch-only wallet</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1499"/> - <source>Enter new password again</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1507"/> - <source>passwords do not match</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1551"/> - <source>invalid arguments. Please use start_mining [<number_of_threads>], <number_of_threads> should be from 1 to </source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1560"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1711"/> <source>Mining started in daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1562"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1713"/> <source>mining has NOT been started: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1577"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1728"/> <source>Mining stopped in daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1579"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1730"/> <source>mining has NOT been stopped: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1594"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1745"/> <source>Blockchain saved</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1609"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1621"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1633"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1760"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1777"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1789"/> <source>Height </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1610"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1622"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1634"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1761"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1778"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1790"/> <source>transaction </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1611"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1762"/> <source>received </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1623"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1779"/> <source>spent </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1635"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1791"/> <source>unsupported transaction format</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1652"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1808"/> <source>Starting refresh...</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1665"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1821"/> <source>Refresh done, blocks received: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1985"/> - <source>you have cancelled the transfer request</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1991"/> - <source>failed to get a Monero address from: </source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1997"/> - <source>not yet supported: Multiple Monero addresses found for given URL: </source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2002"/> - <source>wrong address: </source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2070"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2575"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2278"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2799"/> <source>payment id has invalid format, expected 16 or 64 character hex string: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2085"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2293"/> <source>bad locked_blocks parameter:</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2090"/> - <source>Locked blocks too high, max 1000000 (Ë4 yrs)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2109"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2597"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2320"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2824"/> <source>a single transaction cannot use more than one payment id: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2118"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2606"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2329"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2833"/> <source>failed to set up payment id, though it was decoded correctly</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2138"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2615"/> - <source>No payment id is included with this transaction. Is this okay? (Y/Yes/N/No)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2143"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2227"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2406"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2620"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2665"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2354"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2449"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2629"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2847"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2894"/> <source>transaction cancelled.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2200"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2417"/> <source>Sending %s. </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2203"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2420"/> <source>Your transaction needs to be split into %llu transactions. This will result in a transaction fee being applied to each transaction, for a total fee of %s</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2209"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2426"/> <source>The transaction fee is %s</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2212"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2429"/> <source>, of which %s is dust from change</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2213"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2220"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2430"/> <source>.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2213"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2430"/> <source>A total of %s from dust change will be sent to dust address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2218"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2435"/> <source>. This transaction will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2220"/> - <source>Is this okay? (Y/Yes/N/No)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2241"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2419"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2678"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2463"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2642"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2907"/> <source>Failed to write transaction(s) to file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2245"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2423"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2682"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2467"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2646"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2911"/> <source>Unsigned transaction(s) successfully written to file: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2280"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2458"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2717"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2960"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2502"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2681"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2946"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3233"/> <source>Not enough money in unlocked balance</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2289"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2467"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2726"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2969"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2511"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2690"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2955"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3242"/> <source>Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2309"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2487"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2746"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2531"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2710"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2975"/> <source>Reason: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2321"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2499"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2758"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2543"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2722"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2987"/> <source>failed to find a suitable way to split transactions</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2376"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2599"/> <source>No unmixable outputs found</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2583"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2807"/> <source>No address given</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2635"/> - <source>No outputs found</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2817"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3089"/> <source>Claimed change does not go to a paid address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2822"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3094"/> <source>Claimed change is larger than payment to the change address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2827"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3099"/> <source>Change does to more than one address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2839"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3111"/> <source>sending %s to %s</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2845"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3117"/> <source>with no destinations</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2891"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3164"/> <source>Failed to sign transaction</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2897"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3170"/> <source>Failed to sign transaction: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2923"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3196"/> <source>Failed to load transaction from file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2940"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3213"/> <source>daemon is busy. Please try later</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1679"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1925"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2269"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2447"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2706"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2949"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1835"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2081"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2491"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2670"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2935"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3222"/> <source>RPC error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1689"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2331"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2509"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2768"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3008"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="302"/> + <source>wallet is watch-only and has no spend key</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="380"/> + <source>Your original password was incorrect.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="394"/> + <source>Error with wallet rewrite: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="490"/> + <source>priority must be 0, 1, 2, 3, or 4 </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="502"/> + <source>priority must be 0, 1, 2, 3,or 4</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="517"/> + <source>priority must be 0, 1, 2 3,or 4</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="606"/> + <source>invalid unit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="624"/> + <source>invalid count: must be an unsigned integer</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="642"/> + <source>invalid value</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="688"/> + <source>Same as transfer, but using an older transaction building algorithm</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="692"/> + <source>sweep_all [mixin] address [payment_id] - Send all unlocked balance to an address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="693"/> + <source>donate [<mixin_count>] <amount> [payment_id] - Donate <amount> to the development team (donate.getmonero.org)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="696"/> + <source>set_log <level>|<categories> - Change current log detail (level must be <0-4>)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="699"/> + <source>address_book [(add (<address> [pid <long or short payment id>])|<integrated address> [<description possibly with whitespaces>])|(delete <index>)] - Print all entries in the address book, optionally adding/deleting an entry to/from it</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="705"/> + <source>Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-mixin <n> - set default mixin (default is 4); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [0|1|2|3|4] - default/unimportant/normal/elevated/priority fee; confirm-missing-payment-id <1|0>; ask-password <1|0>; unit <monero|millinero|micronero|nanonero|piconero> - set default monero (sub-)unit; min-output-count [n] - try to keep at least that many outputs of value at least min-output-value; min-output-value [n] - try to keep at least min-output-count outputs of at least that value - merge-destinations <1|0> - whether to merge multiple payments to the same destination address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="709"/> + <source>show_transfers [in|out|pending|failed|pool] [<min_height> [<max_height>]] - Show incoming/outgoing transfers within an optional height range</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="710"/> + <source>unspent_outputs [<min_amount> <max_amount>] - Show unspent outputs within an optional amount range)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="721"/> + <source>Show information about a transfer to/from this address</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="722"/> + <source>Change wallet password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="778"/> + <source>set print-ring-members: needs an argument (0 or 1)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="844"/> + <source>set priority: needs an argument: 0, 1, 2, 3, or 4</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="870"/> + <source>set ask-password: needs an argument (0 or 1)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="883"/> + <source>set unit: needs an argument (monero, millinero, micronero, nanop, piconero)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="896"/> + <source>set min-outputs-count: needs an argument (unsigned integer)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="909"/> + <source>set min-outputs-value: needs an argument (unsigned integer)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="922"/> + <source>set merge-destinations: needs an argument (0 or 1)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="941"/> + <source>usage: set_log <log_level_number_0-4> | <categories></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="1007"/> + <source>(Y/Yes/N/No): </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="1254"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1281"/> + <source>bad m_restore_height parameter: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="1259"/> + <source>date format must be YYYY-MM-DD</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="1272"/> + <source>Restore height is: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="1273"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2442"/> + <source>Is this okay? (Y/Yes/N/No): </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="1305"/> + <source>Daemon is local, assuming trusted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="1655"/> + <source>Password for new watch-only wallet</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="1702"/> + <source>invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery], <number_of_threads> should be from 1 to </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="1845"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2553"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2732"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2997"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3281"/> <source>internal error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1694"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1930"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2336"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2514"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2773"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3013"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1850"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2086"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2558"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2737"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3002"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3286"/> <source>unexpected error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1699"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1935"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2341"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2519"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2778"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3018"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1855"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2091"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2563"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2742"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3007"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3291"/> <source>unknown error</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1704"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1860"/> <source>refresh failed: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1704"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1860"/> <source>Blocks received: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1729"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1885"/> <source>unlocked balance: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1778"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1826"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1934"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1982"/> <source>amount</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1778"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1934"/> <source>spent</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1778"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1934"/> <source>global index</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1778"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1934"/> <source>tx id</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1800"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1956"/> <source>No incoming transfers</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1804"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1960"/> <source>No incoming available transfers</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1808"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1964"/> <source>No incoming unavailable transfers</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1819"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1975"/> <source>expected at least one payment_id</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1826"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1982"/> <source>payment</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1826"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1982"/> <source>transaction</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1826"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1982"/> <source>height</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1826"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1982"/> <source>unlock time</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1838"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1994"/> <source>No payments with id </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1890"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2163"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2046"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2112"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2374"/> <source>failed to get blockchain height: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2036"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2102"/> + <source>failed to connect to the daemon</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="2120"/> + <source> +Transaction %llu/%llu: txid=%s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="2130"/> + <source> +Input %llu/%llu: amount=%s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="2146"/> + <source>failed to get output: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="2154"/> + <source>output key's originating block height shouldn't be higher than the blockchain height</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="2158"/> + <source> +Originating block heights: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="2173"/> + <source> +|</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="2173"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3788"/> + <source>| +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="2190"/> + <source> +Warning: Some input keys being spent are from </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="2192"/> + <source>, which can break the anonymity of ring signature. Make sure this is intentional!</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="2244"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3019"/> <source>wrong number of arguments</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2881"/> - <source>This is a watch only wallet</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="2349"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2842"/> + <source>No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1963"/> - <source>DNSSEC validation passed</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="2392"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2862"/> + <source>No outputs found, or daemon is not ready</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="223"/> - <source>true</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="2495"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2674"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2939"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3226"/> + <source>failed to get random outputs to mix: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="252"/> - <source>failed to parse refresh type</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="2614"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2879"/> + <source>Sweeping %s in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No): </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="309"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="341"/> - <source>wallet is watch-only and has no seed</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="2620"/> + <source>Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No): </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="331"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="346"/> - <source>wallet is non-deterministic and has no seed</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="3051"/> + <source>Donating </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="403"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="433"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="491"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="615"/> - <source>wallet is watch-only and cannot transfer</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="3129"/> + <source>Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. %sIs this okay? (Y/Yes/N/No): </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="440"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="446"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="475"/> - <source>mixin must be an integer >= 2</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="3153"/> + <source>This is a watch only wallet</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="480"/> - <source>could not change default mixin</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="4316"/> + <source>usage: show_transfer <txid></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="498"/> - <source>priority must be 0, 1, 2, or 3 </source> + <location filename="../src/simplewallet/simplewallet.cpp" line="4430"/> + <source>Transaction ID not found</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="510"/> - <source>priority must be 0, 1, 2, or 3</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="227"/> + <source>true</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="537"/> - <source>priority must be 0, 1, 2 or 3</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="256"/> + <source>failed to parse refresh type</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="542"/> - <source>could not change default priority</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="320"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="352"/> + <source>wallet is watch-only and has no seed</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="656"/> - <source>Synchronize transactions and balance</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="343"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="357"/> + <source>wallet is non-deterministic and has no seed</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="658"/> - <source>incoming_transfers [available|unavailable] - Show incoming transfers, all or filtered by availability</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="427"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="444"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="565"/> + <source>wallet is watch-only and cannot transfer</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="659"/> - <source>payments <PID_1> [<PID_2> ... <PID_N>] - Show payments for given payment ID[s]</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="451"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="457"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="473"/> + <source>mixin must be an integer >= 2</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="661"/> - <source>transfer [<mixin_count>] <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of extra inputs to include for untraceability (from 2 to maximum available)</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="478"/> + <source>could not change default mixin</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="662"/> - <source>Same as transfer_original, but using a new transaction building algorithm</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="522"/> + <source>could not change default priority</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="663"/> - <source>locked_transfer [<mixin_count>] <addr> <amount> <lockblocks>(Number of blocks to lock the transaction for, max 1000000) [<payment_id>]</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="683"/> + <source>Synchronize transactions and balance</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="664"/> - <source>Send all unmixable outputs to yourself with mixin 0</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="685"/> + <source>incoming_transfers [available|unavailable] - Show incoming transfers, all or filtered by availability</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="665"/> - <source>sweep_all [mixin] address [payment_id] - Send all unlocked balance an address</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="686"/> + <source>payments <PID_1> [<PID_2> ... <PID_N>] - Show payments for given payment ID[s]</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="666"/> - <source>Sign a transaction from a file</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="689"/> + <source>transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command "set priority") is used. <mixin_count> is the number of extra inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="667"/> - <source>Submit a signed transaction from a file</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="690"/> + <source>locked_transfer [<mixin_count>] <addr> <amount> <lockblocks>(Number of blocks to lock the transaction for, max 1000000) [<payment_id>]</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="691"/> + <source>Send all unmixable outputs to yourself with mixin 0</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="668"/> - <source>set_log <level> - Change current log detail level, <0-4></source> + <location filename="../src/simplewallet/simplewallet.cpp" line="694"/> + <source>Sign a transaction from a file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="670"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="695"/> + <source>Submit a signed transaction from a file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="698"/> <source>integrated_address [PID] - Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="671"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="700"/> <source>Save wallet data</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="672"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="701"/> <source>Save a watch-only keys file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="673"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="702"/> <source>Display private view key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="674"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="703"/> <source>Display private spend key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="675"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="704"/> <source>Display Electrum-style mnemonic seed</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="676"/> - <source>Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-mixin <n> - set default mixin (default is 4); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [1|2|3] - normal/elevated/priority fee; confirm-missing-payment-id <1|0></source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="677"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="706"/> <source>Rescan blockchain for spent outputs</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="678"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="707"/> <source>Get transaction key (r) for a given <txid></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="679"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="708"/> <source>Check amount going to <address> in <txid></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="680"/> - <source>show_transfers [in|out] [<min_height> [<max_height>]] - Show incoming/outgoing transfers within an optional height range</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="681"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="711"/> <source>Rescan blockchain from scratch</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="682"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="712"/> <source>Set an arbitrary string note for a txid</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="683"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="713"/> <source>Get a string note for a txid</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="684"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="714"/> <source>Show wallet status information</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="685"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="715"/> <source>Sign the contents of a file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="686"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="716"/> <source>Verify a signature on the contents of a file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="687"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="717"/> <source>Export a signed set of key images</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="688"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="718"/> <source>Import signed key images list and verify their spent status</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="689"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="719"/> <source>Export a set of outputs owned by this wallet</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="690"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="720"/> <source>Import set of outputs owned by this wallet</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="744"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="791"/> <source>set store-tx-info: needs an argument (0 or 1)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="759"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="804"/> <source>set default-mixin: needs an argument (integer >= 2)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="774"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="817"/> <source>set auto-refresh: needs an argument (0 or 1)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="789"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="830"/> <source>set refresh-type: needs an argument:</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="790"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="831"/> <source>full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="805"/> - <source>set priority: needs an argument: 0, 1, 2, or 3</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="820"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="857"/> <source>set confirm-missing-payment-id: needs an argument (0 or 1)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="841"/> - <source>usage: set_log <log_level_number_0-4></source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="872"/> - <source>Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created. -Wallet file name (or Ctrl-C to quit): </source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="882"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="972"/> <source>Wallet name not valid. Please try again or use Ctrl-C to quit.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="899"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="989"/> <source>Wallet and key files found, loading...</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="905"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="995"/> <source>Key file found but not wallet file. Regenerating...</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="911"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1001"/> <source>Key file not found. Failed to open wallet: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="916"/> - <source>No wallet/key file found with that name. Confirm creation of new wallet named: </source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="917"/> - <source>(y)es/(n)o: </source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="925"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1015"/> <source>Generating new wallet...</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="959"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1049"/> <source>can't specify more than one of --generate-new-wallet="wallet_name", --wallet-file="wallet_name", --generate-from-view-key="wallet_name", --generate-from-json="jsonfilename" and --generate-from-keys="wallet_name"</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="975"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1065"/> <source>can't specify both --restore-deterministic-wallet and --non-deterministic</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="993"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1083"/> <source>Electrum-style word list failed verification</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1008"/> - <source>bad m_restore_height parameter:</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1021"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1038"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1073"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1090"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1106"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1095"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1112"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1147"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1164"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1180"/> <source>No data supplied, cancelled</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1029"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1081"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3099"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3639"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1103"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1155"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2312"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2816"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3373"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3921"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4112"/> <source>failed to parse address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1044"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1112"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1118"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1186"/> <source>failed to parse view key secret key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1054"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1130"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1128"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1204"/> <source>failed to verify view key secret key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1058"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1134"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1132"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1208"/> <source>view key does not match standard address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1063"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1138"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1151"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1137"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1212"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1225"/> <source>account creation failed</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1096"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1170"/> <source>failed to parse spend key secret key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1122"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1196"/> <source>failed to verify spend key secret key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1126"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1200"/> <source>spend key does not match standard address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1158"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1296"/> <source>failed to open account</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1201"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1354"/> <source>Daemon either is not started or wrong port was passed. Please make sure daemon is running or restart the wallet with the correct daemon address.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1245"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1250"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1398"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1403"/> <source>invalid language choice passed. Please try again. </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1306"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1472"/> <source>View key: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1321"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1487"/> <source>Your wallet has been generated! To start synchronizing with the daemon, use "refresh" command. Use "help" command to see the list of available commands. @@ -1254,509 +1611,623 @@ your wallet again (your wallet keys are NOT at risk in any case). <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1428"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1594"/> <source>You may want to remove the file "%s" and try again</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1454"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1620"/> <source>failed to deinitialize wallet</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1520"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1898"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1672"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2054"/> <source>this command requires a trusted daemon. Enable with --trusted-daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1596"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1747"/> <source>blockchain can't be saved: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1670"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1912"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2260"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2438"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2697"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1826"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2068"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2482"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2661"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2926"/> <source>daemon is busy. Please try again later.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1674"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1916"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2264"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2442"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2701"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1830"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2072"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2486"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2665"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2930"/> <source>no connection to daemon. Please make sure daemon is running.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1684"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1840"/> <source>refresh error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1728"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1884"/> <source>Balance: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1777"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1933"/> <source>pubkey</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1777"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1933"/> <source>key image</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1778"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="1788"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1934"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1944"/> <source>unlocked</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1778"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1934"/> <source>ringct</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1787"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1943"/> <source>T</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1787"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1943"/> <source>F</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1788"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1944"/> <source>locked</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1789"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1945"/> <source>RingCT</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1789"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="1945"/> <source>-</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1859"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2015"/> <source>payment ID has invalid format, expected 16 or 64 character hex string: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1920"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2076"/> <source>failed to get spent status</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1967"/> - <source>WARNING: DNSSEC validation was unsuccessful, this address may not be correct!</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="2298"/> + <source>Locked blocks too high, max 1000000 (˜4 yrs)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1970"/> - <source>For URL: </source> + <location filename="../src/simplewallet/simplewallet.cpp" line="3867"/> + <source>usage: integrated_address [payment ID]</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1972"/> - <source> Monero Address = </source> + <location filename="../src/simplewallet/simplewallet.cpp" line="3890"/> + <source>Integrated address: account %s, payment ID %s</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="1974"/> - <source>Is this OK? (Y/n) </source> + <location filename="../src/simplewallet/simplewallet.cpp" line="3895"/> + <source>Standard address: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3475"/> - <source>usage: integrated_address [payment ID]</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="3900"/> + <source>failed to parse payment ID or address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3498"/> - <source>Integrated address: account %s, payment ID %s</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="3911"/> + <source>usage: address_book [(add (<address> [pid <long or short payment id>])|<integrated address> [<description possibly with whitespaces>])|(delete <index>)]</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3503"/> - <source>Standard address: </source> + <location filename="../src/simplewallet/simplewallet.cpp" line="3943"/> + <source>failed to parse payment ID</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3508"/> - <source>failed to parse payment ID or address</source> + <location filename="../src/simplewallet/simplewallet.cpp" line="3961"/> + <source>failed to parse index</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3516"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3969"/> + <source>Address book is empty.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3975"/> + <source>Index: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3976"/> + <source>Address: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3977"/> + <source>Payment ID: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3978"/> + <source>Description: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3988"/> <source>usage: set_tx_note [txid] free text note</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3544"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4016"/> <source>usage: get_tx_note [txid]</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3594"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4066"/> <source>usage: sign <filename></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3599"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4071"/> <source>wallet is watch-only and cannot sign</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3607"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3630"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3717"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3840"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4080"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4103"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4247"/> <source>failed to read file </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3619"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4092"/> <source>usage: verify <filename> <address> <signature></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3646"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4119"/> <source>Bad signature from </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3650"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4123"/> <source>Good signature from </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3659"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4132"/> <source>usage: export_key_images <filename></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3664"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4137"/> <source>wallet is watch-only and cannot export key images</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3689"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3812"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4147"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4219"/> <source>failed to save file </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3700"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4158"/> <source>Signed key images exported to </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3708"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4166"/> <source>usage: import_key_images <filename></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3790"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4196"/> <source>usage: export_outputs <filename></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3823"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4230"/> <source>Outputs exported to </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3831"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4238"/> <source>usage: import_outputs <filename></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2127"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2338"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3691"/> <source>amount is wrong: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2128"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2339"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3692"/> <source>expected number from 0 to </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2252"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2474"/> <source>Money successfully sent, transaction </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2944"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3217"/> <source>no connection to daemon. Please, make sure daemon is running.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2273"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2451"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2710"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2953"/> - <source>failed to get random outputs to mix</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2294"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2472"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2731"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2974"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2516"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2695"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2960"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3247"/> <source>not enough outputs for specified mixin_count</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2297"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2475"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2734"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2977"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2519"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2698"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2963"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3250"/> <source>output amount</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2297"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2475"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2734"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2977"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2519"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2698"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2963"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3250"/> <source>found outputs to mix</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2302"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2480"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2739"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2982"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2524"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2703"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2968"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3255"/> <source>transaction was not constructed</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2306"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2484"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2743"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2986"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2528"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2707"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2972"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3259"/> <source>transaction %s was rejected by daemon with status: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2317"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2495"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2754"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2994"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2539"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2718"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2983"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3267"/> <source>one of destinations is zero</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2998"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3271"/> <source>Failed to find a suitable way to split transactions</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2326"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2504"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2763"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3003"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2548"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2727"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2992"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3276"/> <source>unknown transfer error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2389"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2612"/> <source>Sweeping </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2391"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2650"/> - <source>Sweeping %s in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2397"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2656"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2885"/> <source>Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No)</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2430"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2689"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="2932"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2653"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="2918"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3205"/> <source>Money successfully sent, transaction: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2851"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3123"/> <source>%s change to %s</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2854"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3126"/> <source>no change</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2857"/> - <source>Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. %sIs this okay? (Y/Yes/N/No)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="2908"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3181"/> <source>Transaction successfully signed to file </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3029"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3302"/> <source>usage: get_tx_key <txid></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3036"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3073"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3523"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3551"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3310"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3347"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3995"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4023"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4323"/> <source>failed to parse txid</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3047"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3321"/> <source>Tx key: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3052"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3326"/> <source>no tx keys found for this txid</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3062"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3336"/> <source>usage: check_tx_key <txid> <txkey> <address></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3082"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3089"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3356"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3363"/> <source>failed to parse tx key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3109"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3383"/> <source>failed to get transaction from daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3120"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3394"/> <source>failed to parse transaction from daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3127"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3401"/> <source>failed to validate transaction from daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3132"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3406"/> <source>failed to get the right transaction from daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3139"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3413"/> <source>failed to generate key derivation from supplied parameters</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3195"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3469"/> <source>error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3201"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3475"/> <source>received</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3201"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3475"/> <source>in txid</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3205"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3479"/> <source>received nothing in txid</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3209"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3483"/> <source>WARNING: this transaction is not yet included in the blockchain!</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3218"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3492"/> <source>This transaction has %u confirmations</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3222"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3496"/> <source>WARNING: failed to determine number of confirmations!</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3262"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3536"/> <source>usage: show_transfers [in|out|all|pending|failed] [<min_height> [<max_height>]]</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3301"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3575"/> <source>bad min_height parameter:</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3313"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3587"/> <source>bad max_height parameter:</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3359"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3633"/> <source>in</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3359"/> - <location filename="../src/simplewallet/simplewallet.cpp" line="3397"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3633"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3671"/> <source>out</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3397"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3671"/> <source>failed</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3397"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3671"/> <source>pending</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3450"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3682"/> + <source>usage: unspent_outputs [<min_amount> <max_amount>]</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3697"/> + <source><min_amount> should be smaller than <max_amount></source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3729"/> + <source> +Amount: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3729"/> + <source>, number of keys: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3734"/> + <source> </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3739"/> + <source> +Min block height: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3740"/> + <source> +Max block height: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3741"/> + <source> +Min amount found: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3742"/> + <source> +Max amount found: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3743"/> + <source> +Total count: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3783"/> + <source> +Bin size: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3784"/> + <source> +Outputs per *: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3786"/> + <source>count + ^ +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3788"/> + <source> |</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3790"/> + <source> +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3790"/> + <source>+--> block height +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3791"/> + <source> ^</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3791"/> + <source>^ +</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3792"/> + <source> </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/simplewallet/simplewallet.cpp" line="3842"/> <source>wallet</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3481"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3873"/> <source>Random payment ID: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3482"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="3874"/> <source>Matching integrated address: </source> <translation type="unfinished"></translation> </message> @@ -1764,274 +2235,348 @@ your wallet again (your wallet keys are NOT at risk in any case). <context> <name>sw</name> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="102"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="106"/> <source>Generate new wallet and save it to <arg></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="103"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="107"/> <source>Generate incoming-only wallet from view key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="104"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="108"/> <source>Generate wallet from private keys</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="106"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="110"/> <source>Specify Electrum seed for wallet recovery/creation</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="107"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="111"/> <source>Recover wallet using Electrum-style mnemonic seed</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="108"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="112"/> <source>Create non-deterministic view and spend keys</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="109"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="113"/> <source>Enable commands which rely on a trusted daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="110"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="114"/> <source>Allow communicating with a daemon that uses a different RPC version</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="111"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="115"/> <source>Restore from specific blockchain height</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="122"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="126"/> <source>daemon is busy. Please try again later.</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="131"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="135"/> <source>possibly lost connection to daemon</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="212"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="216"/> <source>Error: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3948"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4487"/> <source>Failed to initialize wallet</source> <translation type="unfinished"></translation> </message> </context> <context> + <name>tools::dns_utils</name> + <message> + <location filename="../src/common/dns_utils.cpp" line="430"/> + <source>DNSSEC validation passed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/common/dns_utils.cpp" line="434"/> + <source>WARNING: DNSSEC validation was unsuccessful, this address may not be correct!</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/common/dns_utils.cpp" line="437"/> + <source>For URL: </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/common/dns_utils.cpp" line="439"/> + <source> Monero Address = </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/common/dns_utils.cpp" line="441"/> + <source>Is this OK? (Y/n) </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/common/dns_utils.cpp" line="451"/> + <source>you have cancelled the transfer request</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>tools::wallet2</name> <message> - <location filename="../src/wallet/wallet2.cpp" line="102"/> + <location filename="../src/wallet/wallet2.cpp" line="106"/> <source>Use daemon instance at <host>:<port></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="103"/> + <location filename="../src/wallet/wallet2.cpp" line="107"/> <source>Use daemon instance at host <arg> instead of localhost</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="104"/> + <location filename="../src/wallet/wallet2.cpp" line="108"/> + <location filename="../src/wallet/wallet2.cpp" line="458"/> <source>Wallet password</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="105"/> + <location filename="../src/wallet/wallet2.cpp" line="109"/> <source>Wallet password file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="106"/> + <location filename="../src/wallet/wallet2.cpp" line="110"/> <source>Use daemon instance at port <arg> instead of 18081</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="107"/> + <location filename="../src/wallet/wallet2.cpp" line="112"/> <source>For testnet. Daemon must also be launched with --testnet flag</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="108"/> + <location filename="../src/wallet/wallet2.cpp" line="113"/> <source>Restricts to view-only commands</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="147"/> + <location filename="../src/wallet/wallet2.cpp" line="152"/> <source>can't specify daemon host or port more than once</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="171"/> + <location filename="../src/wallet/wallet2.cpp" line="188"/> <source>can't specify more than one of --password and --password-file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="189"/> + <location filename="../src/wallet/wallet2.cpp" line="204"/> <source>the password file specified could not be read</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="206"/> + <location filename="../src/wallet/wallet2.cpp" line="458"/> + <source>Enter new wallet password</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/wallet2.cpp" line="462"/> <source>failed to read wallet password</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="219"/> + <location filename="../src/wallet/wallet2.cpp" line="225"/> <source>Failed to load file </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="225"/> + <location filename="../src/wallet/wallet2.cpp" line="111"/> + <source>Specify username[:password] for daemon RPC client</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/wallet2.cpp" line="231"/> <source>Failed to parse JSON</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="232"/> + <location filename="../src/wallet/wallet2.cpp" line="238"/> <source>Version %u too new, we can only grok up to %u</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="250"/> + <location filename="../src/wallet/wallet2.cpp" line="256"/> <source>failed to parse view key secret key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="256"/> - <location filename="../src/wallet/wallet2.cpp" line="323"/> - <location filename="../src/wallet/wallet2.cpp" line="365"/> + <location filename="../src/wallet/wallet2.cpp" line="262"/> + <location filename="../src/wallet/wallet2.cpp" line="329"/> + <location filename="../src/wallet/wallet2.cpp" line="371"/> <source>failed to verify view key secret key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="268"/> + <location filename="../src/wallet/wallet2.cpp" line="274"/> <source>failed to parse spend key secret key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="274"/> - <location filename="../src/wallet/wallet2.cpp" line="335"/> - <location filename="../src/wallet/wallet2.cpp" line="386"/> + <location filename="../src/wallet/wallet2.cpp" line="280"/> + <location filename="../src/wallet/wallet2.cpp" line="341"/> + <location filename="../src/wallet/wallet2.cpp" line="392"/> <source>failed to verify spend key secret key</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="287"/> + <location filename="../src/wallet/wallet2.cpp" line="293"/> <source>Electrum-style word list failed verification</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="298"/> + <location filename="../src/wallet/wallet2.cpp" line="304"/> <source>At least one of Electrum-style word list and private view key must be specified</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="303"/> + <location filename="../src/wallet/wallet2.cpp" line="309"/> <source>Both Electrum-style word list and private key(s) specified</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="316"/> + <location filename="../src/wallet/wallet2.cpp" line="322"/> <source>invalid address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="327"/> + <location filename="../src/wallet/wallet2.cpp" line="333"/> <source>view key does not match standard address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="339"/> + <location filename="../src/wallet/wallet2.cpp" line="345"/> <source>spend key does not match standard address</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="348"/> + <location filename="../src/wallet/wallet2.cpp" line="354"/> <source>Cannot create deprecated wallets from JSON</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet2.cpp" line="395"/> + <location filename="../src/wallet/wallet2.cpp" line="401"/> <source>failed to generate new wallet: </source> <translation type="unfinished"></translation> </message> + <message> + <location filename="../src/wallet/wallet2.cpp" line="5167"/> + <source>failed to read file </source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>tools::wallet_rpc_server</name> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="100"/> - <source>Invalid IP address given for rpc-bind-ip argument</source> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="122"/> + <source>Daemon is local, assuming trusted</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="107"/> - <source>The rpc-bind-ip value is listening for unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --confirm-external-bind</source> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="132"/> + <source>Cannot specify --</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1161"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="132"/> + <source> and --</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="159"/> + <source>Failed to create file </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="159"/> + <source>. Check permissions or remove file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="170"/> + <source>Error writing to file </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="173"/> + <source>RPC username/password is stored in file </source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1517"/> <source>Can't specify more than one of --wallet-file and --generate-from-json</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1167"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1523"/> <source>Must specify --wallet-file or --generate-from-json</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1171"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1527"/> <source>Loading wallet...</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1196"/> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1219"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1552"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1575"/> <source>Storing wallet...</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1198"/> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1221"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1554"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1577"/> <source>Stored ok</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1201"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1557"/> <source>Loaded ok</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1205"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1561"/> <source>Wallet initialization failed: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1210"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1566"/> <source>Failed to initialize wallet rpc server</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1214"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1570"/> <source>Starting wallet rpc server</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1216"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1572"/> <source>Stopped wallet rpc server</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1225"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1581"/> <source>Failed to store wallet: </source> <translation type="unfinished"></translation> </message> @@ -2039,54 +2584,59 @@ your wallet again (your wallet keys are NOT at risk in any case). <context> <name>wallet_args</name> <message> - <location filename="../src/simplewallet/simplewallet.cpp" line="3915"/> - <location filename="../src/wallet/wallet_rpc_server.cpp" line="1131"/> + <location filename="../src/simplewallet/simplewallet.cpp" line="4453"/> + <location filename="../src/wallet/wallet_rpc_server.cpp" line="1486"/> <source>Wallet options</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_args.cpp" line="56"/> + <location filename="../src/wallet/wallet_args.cpp" line="59"/> <source>Generate wallet from JSON format file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_args.cpp" line="60"/> + <location filename="../src/wallet/wallet_args.cpp" line="63"/> <source>Use wallet <arg></source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_args.cpp" line="82"/> + <location filename="../src/wallet/wallet_args.cpp" line="87"/> <source>Max number of threads to use for a parallel job</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_args.cpp" line="83"/> + <location filename="../src/wallet/wallet_args.cpp" line="88"/> <source>Specify log file</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_args.cpp" line="92"/> + <location filename="../src/wallet/wallet_args.cpp" line="89"/> + <source>Config file</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../src/wallet/wallet_args.cpp" line="98"/> <source>General options</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_args.cpp" line="106"/> - <source>unexpected empty log file name in presence of non-empty file path</source> + <location filename="../src/wallet/wallet_args.cpp" line="128"/> + <source>Can't find config file </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_args.cpp" line="131"/> - <source>Usage:</source> + <location filename="../src/wallet/wallet_args.cpp" line="172"/> + <source>Logging to: </source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_args.cpp" line="178"/> - <source>default_log: </source> + <location filename="../src/wallet/wallet_args.cpp" line="173"/> + <source>Logging to %s</source> <translation type="unfinished"></translation> </message> <message> - <location filename="../src/wallet/wallet_args.cpp" line="179"/> - <source>Logging at log level %d to %s</source> + <location filename="../src/wallet/wallet_args.cpp" line="153"/> + <source>Usage:</source> <translation type="unfinished"></translation> </message> </context> diff --git a/utils/build_scripts/windows.bat b/utils/build_scripts/windows.bat index f73dc6e5f..c78086e6c 100644 --- a/utils/build_scripts/windows.bat +++ b/utils/build_scripts/windows.bat @@ -1,4 +1,4 @@ -:: Copyright (c) 2014-2016, The Monero Project +:: Copyright (c) 2014-2017, The Monero Project :: :: All rights reserved. :: diff --git a/utils/gpg_keys/hyc.asc b/utils/gpg_keys/hyc.asc index 4d174e989..0bf10caff 100644 --- a/utils/gpg_keys/hyc.asc +++ b/utils/gpg_keys/hyc.asc @@ -41,4 +41,4 @@ m/RwmduzBwMXzMikPOyRwYhPBBgRAgAPBQJWkBA0AhsMBQkSzAMAAAoJEP0qcLRK sRunqSUAnRenNWORvzTRRy0qmF5xVFlDIUGpAJ4pUZgkE7YRyjSzdhxcaRBOjAQa GQ== =sUzZ ------END PGP PUBLIC KEY BLOCK-----
\ No newline at end of file +-----END PGP PUBLIC KEY BLOCK----- diff --git a/utils/gpg_keys/jaquee.asc b/utils/gpg_keys/jaquee.asc index f1199dd25..b1a6eff2f 100644 --- a/utils/gpg_keys/jaquee.asc +++ b/utils/gpg_keys/jaquee.asc @@ -27,4 +27,4 @@ eKSuF58aYVROY9FUNhFsvgjGI/sYNQFj0DoexLe28f4/cFFVpZvb/kJYqm4QauyK 5SMyvTTu8PD+pZpLM9MFoQ+Jk+EmDzSUlarwnt75O6kMLNLPFJ8hCOiN2RNK2PKk iTpY5CQtKsGQDRA7sT6ZaiWO7kkQGO0HSBfmWnZq2G0= =Ky09 ------END PGP PUBLIC KEY BLOCK-----
\ No newline at end of file +-----END PGP PUBLIC KEY BLOCK----- diff --git a/utils/gpg_keys/kenshi84.asc b/utils/gpg_keys/kenshi84.asc new file mode 100644 index 000000000..d2de312ef --- /dev/null +++ b/utils/gpg_keys/kenshi84.asc @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFjtZtQBEADVjtpIAlDUqtWbr6yKikJDkPPhs6f2zByStaxB1GU/ZpuiO1WB +lI4u/k3WyjHwSiPlcSZ0jMn8wy01mWT0qWyN7ZOQ793kifMG+0encEWfPG/HSNym +AFQf0emRMr8WijtxpoBvHkv6BjbBgd57ByV7zoJWQ6giaLyvGvC3LGfuXFdcXjQP +bfi9T5o2J/K3c6FbuYyW0W/8cZq6s5jQdQk5cZMDwtA47qYgc0oUBBrLpii9B1Jz +s0zVoYORO/Wsuc5DwzswshHnHLOyxgppV1cjpJhGozxTRZE9AfCPv01Z3+TScjql +QnRwkCQIbQ0ketEmXm9QsKkkPKNNos+5ImNuarvoq355NTTltTgbTxkxNWQJqSgv +01hNxAYhvSi2QGcMZg0BTR/5SwR5m5nQgesrFuq5xStYFJ+lebj6qBQZF/Du5Gir +d0BFGiZxx4yDLnEk7MRvADs1POlPLHDgnL5K/eEOq/7KehmQQDUez4tyh5k69Hlc +BdPUHjZoIDMl3ym1LOxOrqzjlWhmiSHPvPM1Ytau96QhbSY9UeLQG/anpdrhEiiH +ckSqjSIsB1hVL802L5D0Tcrt174v3b+fZPvPbuqz3GbkoO7eMYK1WtgCQgHq9M2n +mx3jhqi3VQ4aY1gZlB+wefq8nB2X4HAdK2epDp5Ap1V56nQpkwQ/k9N7qQARAQAB +tCFrZW5zaGk4NCA8a2Vuc2hpODRAcHJvdG9ubWFpbC5jaD6JAj4EEwECACgFAljt +ZtQCGwMFCRLMAwAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEAhdCS8fQ9Ua +bk0QANP9nGWuNfvLP2HxaffDq/fzzSDOUtcEDK+v3/2tSku7AsBenCj2Ju7EAKKx +f9J5fMN40xOVhaJ72oKt8rKgG/U+M9F8IC6nmazqv67GacJsL8zAsOwS0iOKeNd0 +wv1nYRNQjlJzO3RhNo0gXLdRj/5trJrQiJIc4hIJU+KQinpq0C5QjIt1979N9j9f +sinsQoLZwY9vKmWhjWUQMv4jeb2j2SmwKsDCbh7+Edzwn/uDQGui7h6Rf3uB7hLl +MTaYAe6/q3B0zh725hSnuZwO4dLo7N7bwP/MWtYxgj0C7JXpA2o7BZRVKfX/mKeI +KP5U8d3gFJsfEGZxDle0BSN8N2Dx/QC5c+G1TD/zdnY4MUaqPs4wMKSLh5VOScb8 +HOvqnk70DDMvh12uqOZsyjorA35vywvxYeF+wF6poM5vv9/DaFYhVFRt6oO+twWc +IAU1uDEK3yrEUiRiEHkG2bxZPFDxjZqzDjWGADLeT+b+KqxW7lWtaXr//AK5KVPv +HCbCXXdi/HJ9lb8vRGMmQzf9vgNBQy3j5GFyFKcKG51yXkp73KxhMq0o5/DaC+n9 +7K33ZoyebKHKgChLup5ZaRiCfYFj3DU5CoIax0gpM+9YjPW2pTucDBlut1K2XHkJ +DGj1hy/tgJgYPe+Cs2RVTwqGwN4L2VVtfGw7F49HpMdNINpguQINBFjtZtQBEAC0 +spVhziTsh8xnBC5lbXXZvVHyPlslyTeCn+uUB79V3ER8HwTVviSV7XNRms19656e +1SpnjPZOalBnZ8mr78KgyTe6Ud7+7mBmvH/M7RwOjB3VIPNL/eLH1IfACc5KNDxd +3/jlwXRjPhD0HPkLsvTxm5hTMrqzD2ptf3cBCbKj/UVGmhPVn+E2gqis3ocOnSSN +De0ycjoY+3AWxiaUIPibgz/dBnhkqgoRuI1hnZ7qRPIVzef+7mrSc77s2AA8pBbl +mr8IebZD8BO1ZVIR34s6dtTxxonmCuZcIOoXRPB0fyqlF/tBzwYOU6kIcwBaKHaG +MOb4AdCmmEQjVF7klmWUJOQI9hISo5jKL5l4q2HD0x3WRgo7/ih443MLu7bqOUYS +G4y7sA5hzC/Y3L9hQ+RyHRzXdlKjx2T5uAheF+oSxWdJfkwUrGgWnatQ/CdD6dev +GkkIcnPp8L39/7+VXrYqhP3e5SAUrxPYB2k+6z/gMf2r+jwfKJYTYihdeeRcznzR +OlbybAmZ2JoKuzKiJ+zyHt71D+XQP37ssWR6CgO1kWDJopYTvhNxcST1DmesQCZ8 +/vV4Bd4CFlwtmlGoRRURYL9uoYUX4xplB/vy+Z4xCVTEEJcngExACuzvHdonOUI3 +QKc0CO5VfxJ+AeEGJj5rW3wUn/YBRDUaitTMsYgmOwARAQABiQIlBBgBAgAPBQJY +7WbUAhsMBQkSzAMAAAoJEAhdCS8fQ9UafU8QAMISseUfr4uR9ECAWtZlxn+jRzlr +YIQh/MV8JGRr5E8+aNjec/I33R003PvWKimaY1yzJcYw+FHq9l+NwcBTPiDDz1ya +KGPoJXCDAMYcgLpsB/yG1zcaU/Dxuw2eJdyFdlGpxSW87bJpp6jgboIQ4rLGo5Za +0i5j+wkhEQACWCKq0GQQhMWCUU2clZy/WjkkhqQ/sgB1zZgeuZoMtsuCKZ3kMrd1 +AY/5riNxnUCsfVfS3kSEarmZZzL62o3Q02v5q2rKEqaJBp/6bWBmOVtIYNbUyJq0 +76QLb4P90jo+LJPfh/XcpKzJOzkfiJfy9pC8+tNuGrzlqu/mdHh7dq4Egk76TLUg +8PvMgv5ndbKdPwxTx8crFFb5XPAsO6LyFofdmByZLHtXvYtiwtEiC8FL04ZYLsiE +9fvr548l/TLn6/+b3EnQ94YdNZFmtQVEY2QugJMmIK4LeaefZcLHtWWovWgIcA/3 +O4zqNetHyEB46Y1LnW+0IY+9LPRR3Vmhnz0RheFWLJE6jCCXmqmCqEA5jp/BR5Vl +nD/x5z0Yist5voIcVJk6z3FvuA1FclKctYM1SmAH2VZ0XfbWZEmveHr/+Z5ucwuC +WmaxcUCDA9ENP9uCJbqEREWCsTNxnvCs9mkF8mqzHwgFB6/uL2qVGieT6ne140RG +RPJhGZ14x9beu7eu +=R4WU +-----END PGP PUBLIC KEY BLOCK----- diff --git a/utils/gpg_keys/luigi1111.asc b/utils/gpg_keys/luigi1111.asc index 4ac3de627..aadaa3c36 100644 --- a/utils/gpg_keys/luigi1111.asc +++ b/utils/gpg_keys/luigi1111.asc @@ -27,4 +27,4 @@ u7dvJ3hLHLbxBFvIbDBMfzev+g6wOGkhT2/7osUACoLwTwzkbXZNSoQHiZ72YA/P 9f3SA5Zc5hGopft96sAAnzk6sm/xCfFO1uj6Yk/H/fd/ZjVzQW+XBUvwM/jIqpuX Ftwy4Ulp0YZT5BpXkHDhosL6o4GelJULMbxreh7nNQ== =j0V6 ------END PGP PUBLIC KEY BLOCK-----
\ No newline at end of file +-----END PGP PUBLIC KEY BLOCK----- diff --git a/utils/munin_plugins/alt_blocks_count b/utils/munin_plugins/alt_blocks_count index cf6b3651b..bcdb01936 100644 --- a/utils/munin_plugins/alt_blocks_count +++ b/utils/munin_plugins/alt_blocks_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/difficulty b/utils/munin_plugins/difficulty index edfd2bf16..0c020ee8d 100644 --- a/utils/munin_plugins/difficulty +++ b/utils/munin_plugins/difficulty @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/grey_peerlist_size b/utils/munin_plugins/grey_peerlist_size index d7b1bd6a7..579918b03 100644 --- a/utils/munin_plugins/grey_peerlist_size +++ b/utils/munin_plugins/grey_peerlist_size @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/height b/utils/munin_plugins/height index 3f79fa302..e092ef6e1 100644 --- a/utils/munin_plugins/height +++ b/utils/munin_plugins/height @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/incoming_connections_count b/utils/munin_plugins/incoming_connections_count index 7232e5736..709be835e 100644 --- a/utils/munin_plugins/incoming_connections_count +++ b/utils/munin_plugins/incoming_connections_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/outgoing_connections_count b/utils/munin_plugins/outgoing_connections_count index 773ce4624..7055d7f84 100644 --- a/utils/munin_plugins/outgoing_connections_count +++ b/utils/munin_plugins/outgoing_connections_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/tx_count b/utils/munin_plugins/tx_count index f2504cd5f..21f7bc1c7 100644 --- a/utils/munin_plugins/tx_count +++ b/utils/munin_plugins/tx_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/tx_pool_size b/utils/munin_plugins/tx_pool_size index 3bf952071..56a0a78a9 100644 --- a/utils/munin_plugins/tx_pool_size +++ b/utils/munin_plugins/tx_pool_size @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/white_peerlist_size b/utils/munin_plugins/white_peerlist_size index 9e5771cc3..5120ec011 100644 --- a/utils/munin_plugins/white_peerlist_size +++ b/utils/munin_plugins/white_peerlist_size @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2016, The Monero Project +# Copyright (c) 2014-2017, The Monero Project # # All rights reserved. # |