Merge pull request #135 from moneroexamples/devel

For monero version 0.14.0.0
pull/148/head
moneroexamples 5 years ago committed by GitHub
commit ca8265f123
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

4
.gitignore vendored

@ -13,4 +13,6 @@ build
.cproject
.project
.settings/
.kdev4/
.vscode/
openmonero.kdev4

4
.gitmodules vendored

@ -0,0 +1,4 @@
[submodule "src/xmregcore"]
path = src/xmregcore
url = https://github.com/moneroexamples/xmregcore
branch = master

@ -0,0 +1,159 @@
# Generated by YCM Generator at 2019-02-04 16:43:28.957650
# This file is NOT licensed under the GPLv3, which is the license for the rest
# of YouCompleteMe.
#
# Here's the license text for this file:
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# 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 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.
#
# For more information, please refer to <http://unlicense.org/>
import os
import ycm_core
flags = [
'-x',
'c++',
'-DASIO_STANDALONE=YES',
'-DBUILD_SSL=TRUE',
'-I/home/mwo2/monero/build',
'-I/home/mwo2/monero/contrib/epee/include',
'-I/home/mwo2/monero/external',
'-I/home/mwo2/monero/external/db_drivers/liblmdb',
'-I/home/mwo2/monero/external/easylogging++',
'-I/home/mwo2/monero/src',
'-I/home/mwo2/openmonero/ext/restbed/source',
'-I/home/mwo2/openmonero/src/xmregcore',
'-I/usr/include/mysql',
'-I/usr/local/include',
'-I/usr/local/include/mysql',
'-I/usr/local/opt/openssl/include',
'-Wall',
'-Weffc++',
'-Wextra',
'-Wno-unknown-pragmas',
'-std=c++14',
'-std=gnu++14',
'-isystem', '/home/mwo2/openmonero/ext/restbed/dependency/asio/asio/include',
'-isystem', '/home/mwo2/openmonero/ext/restbed/dependency/kashmir',
]
# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# You can get CMake to generate this file for you by adding:
# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
# to your CMakeLists.txt file.
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
compilation_database_folder = ''
if os.path.exists( compilation_database_folder ):
database = ycm_core.CompilationDatabase( compilation_database_folder )
else:
database = None
SOURCE_EXTENSIONS = [ '.C', '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
def DirectoryOfThisScript():
return os.path.dirname( os.path.abspath( __file__ ) )
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
if not working_directory:
return list( flags )
new_flags = []
make_next_absolute = False
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
for flag in flags:
new_flag = flag
if make_next_absolute:
make_next_absolute = False
if not flag.startswith( '/' ):
new_flag = os.path.join( working_directory, flag )
for path_flag in path_flags:
if flag == path_flag:
make_next_absolute = True
break
if flag.startswith( path_flag ):
path = flag[ len( path_flag ): ]
new_flag = path_flag + os.path.join( working_directory, path )
break
if new_flag:
new_flags.append( new_flag )
return new_flags
def IsHeaderFile( filename ):
extension = os.path.splitext( filename )[ 1 ]
return extension in [ '.H', '.h', '.hxx', '.hpp', '.hh' ]
def GetCompilationInfoForFile( filename ):
# The compilation_commands.json file generated by CMake does not have entries
# for header files. So we do our best by asking the db for flags for a
# corresponding source file, if any. If one exists, the flags for that file
# should be good enough.
if IsHeaderFile( filename ):
basename = os.path.splitext( filename )[ 0 ]
for extension in SOURCE_EXTENSIONS:
replacement_file = basename + extension
if os.path.exists( replacement_file ):
compilation_info = database.GetCompilationInfoForFile(
replacement_file )
if compilation_info.compiler_flags_:
return compilation_info
return None
return database.GetCompilationInfoForFile( filename )
def FlagsForFile( filename, **kwargs ):
if database:
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object
compilation_info = GetCompilationInfoForFile( filename )
if not compilation_info:
return None
final_flags = MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_ )
else:
relative_to = DirectoryOfThisScript()
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
return {
'flags': final_flags,
'do_cache': True
}

@ -6,6 +6,7 @@ set(PROJECT_NAME
project(${PROJECT_NAME})
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_BUILD_TYPE Debug)
if(CMAKE_SIZEOF_VOID_P EQUAL "4")
add_definitions(-DMDB_VL32)
@ -15,7 +16,9 @@ if (NOT MONERO_DIR)
set(MONERO_DIR ~/monero)
endif()
option(BUILD_TEST "Build tests for the project" OFF)
set( CMAKE_EXPORT_COMPILE_COMMANDS ON )
option(BUILD_TESTS "Build tests for the project" OFF)
message(STATUS MONERO_DIR ": ${MONERO_DIR}")
@ -31,13 +34,15 @@ set(MY_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake"
list(APPEND CMAKE_MODULE_PATH "${MY_CMAKE_DIR}")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/src/xmregcore/cmake")
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} "${MONERO_BUILD_DIR}"
CACHE PATH "Add Monero directory for library searching")
include(MyUtils)
if (${BUILD_TEST})
if (${BUILD_TESTS})
include(CodeCoverage)
APPEND_COVERAGE_COMPILER_FLAGS()
set(COVERAGE_EXCLUDES
@ -47,15 +52,17 @@ if (${BUILD_TEST})
'${CMAKE_SOURCE_DIR}/tests/*')
endif()
find_package(Monero)
find_package(MYSQL)
if (NOT MYSQL_INCLUDE_DIR)
message(SEND_ERROR "MySQL libraries not found! Please install mysql++/mysqlpp libraries")
message(SEND_ERROR "MySQL libraries not found!
Please install mysql++/mysqlpp libraries")
return()
endif()
include_directories(${MYSQL_INCLUDE_DIR})
# include boost headers
@ -79,7 +86,8 @@ link_directories(
create_git_version()
configure_files(${CMAKE_CURRENT_SOURCE_DIR}/config ${CMAKE_CURRENT_BINARY_DIR}/config)
configure_files(${CMAKE_CURRENT_SOURCE_DIR}/config
${CMAKE_CURRENT_BINARY_DIR}/config)
# find boost
@ -96,6 +104,11 @@ find_package(Boost COMPONENTS
REQUIRED)
# add XMREGCORE submodule
set(BUILD_XMREGCORE_TESTS OFF CACHE INTERNAL "")
add_subdirectory(src/xmregcore)
# add src/ subfolder
add_subdirectory(src/)
@ -109,37 +122,24 @@ set(SOURCE_FILES
add_executable(${PROJECT_NAME}
${SOURCE_FILES})
target_include_directories(${PROJECT_NAME}
PRIVATE src/xmregcore)
# include monero headers
target_include_monero_directories(${PROJECT_NAME})
set(LIBRARIES
myxrm
myext
myxrmcore
${Monero_LIBRARIES}
restbed
wallet
cryptonote_core
blockchain_db
cryptonote_protocol
cryptonote_basic
daemonizer
cncrypto
blocks
lmdb
ringct
ringct_basic
common
mnemonics
epee
easylogging
mysqlpp
mysqlclient
device
epee
checkpoints
version
sodium
${Boost_LIBRARIES}
sodium
pthread
unbound
curl
cncrypto
ssl
crypto)
@ -170,7 +170,7 @@ set(LIBRARIES ${LIBRARIES} ${HIDAPI_LIBRARIES})
target_link_libraries(${PROJECT_NAME} ${LIBRARIES})
if (${BUILD_TEST})
if (${BUILD_TESTS})
include_directories(
${CMAKE_SOURCE_DIR}/ext/googletest/googletest/include
${CMAKE_SOURCE_DIR}/ext/googletest/googlemock/include)
@ -178,7 +178,7 @@ endif()
configure_files(${CMAKE_CURRENT_SOURCE_DIR}/sql ${CMAKE_CURRENT_BINARY_DIR}/sql)
if (${BUILD_TEST})
if (${BUILD_TESTS})
enable_testing()
add_subdirectory(ext/googletest)
add_subdirectory(tests)

@ -86,27 +86,11 @@ For other Linux operating systems, the instructions are analogical.
#### Monero download and compilation
Download and compile recent Monero (v 0.13) into your home folder:
```bash
# first install monero dependecines
sudo apt update
sudo apt install git build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libcurl4-openssl-dev libgtest-dev libreadline-dev libzmq3-dev libsodium-dev libhidapi-dev libhidapi-libusb0
# go to home folder
cd ~
# download monero sourced for branch release-v0.13
To download and compile recent Monero follow instructions
in the following link:
git clone --recursive -b release-v0.13 https://github.com/monero-project/monero.git
cd monero/
# checkout latest release of monero v0.13.0.4
git checkount -b v13004 v0.13.0.4
USE_SINGLE_BUILDDIR=1 make
```
https://github.com/moneroexamples/monero-compilation/blob/master/README.md
#### Compilation of the OpenMonero (don't run it yet)
@ -116,14 +100,15 @@ we can just do it now, to see if it compiles. But don't run it yet. It will not
work without database, setup frontend, and synced and running monero blockchain.
```bash
# need mysql++ library gcovr (for code coverage)
sudo apt install libmysql++-dev gcovr
# need mysql++ library
sudo apt install libmysql++-dev
# go to home folder if still in ~/monero
cd ~
git clone https://github.com/moneroexamples/openmonero.git
# download the source code of the devel branch
git clone --recursive https://github.com/moneroexamples/openmonero.git
cd openmonero
@ -183,22 +168,77 @@ frontend files are stored. All these can be changed to suit your requirements.
Go to localhost (http://127.0.0.1) and check if frontend is working.
#### mymonero-core-js (optional)
OpenMonero uses frontend code provided by mymonero.com. Among many files
used, the two crtical ones are binary webassamply
[MyMoneroCoreCpp_WASM.wasm](https://mymonero.com/js/lib/mymonero_core_js/monero_utils/MyMoneroCoreCpp_WASM.wasm) and
the corresponding JavaScript [mymonero-core.js](https://mymonero.com/js/lib/mymonero-core.js) files.
They are used by [send_coins.js](https://mymonero.com/js/controllers/send_coins.js?) for providing
transaction generation functionality.
OpenMonero provides these files here: `./html/js/lib`. They were generated using forked `mymonero-core-js` repo:
https://github.com/moneroexamples/mymonero-core-js/tree/openmonero
However, you can compile them yourself using the orginal repository located at
https://github.com/mymonero/mymonero-core-js.
Below are instructions on how it can be done on Arch Linux.
```
git clone https://github.com/mymonero/mymonero-core-js.git
cd mymonero-core-js/
./bin/update_submodules
npm install
# download boost
wget -c "https://dl.bintray.com/boostorg/release/1.68.0/source/boost_1_68_0.tar.gz" -O /tmp/boost.tar.gz && mkdir -p ./contrib && tar xzvf /tmp/boost.tar.gz -C ./contrib && mv ./contrib/boost_1_68_0/ ./contrib/boost-sdk
# set EMSCRIPTEN paths (for this, you need to have EMSCRIPTEN setup, e.g. in your home folder)
# http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html
source ~/emsdk/emsdk_env.sh
# compile boost
./bin/build-boost-emscripten.sh
# compile mymonero-core-js
./bin/build-emcpp.sh
# generate mymonero-core.js
./bin/package_browser_js
```
The above instructions should produce `mymonero-core.js`
and `mymonero_core_js/monero_utils/MyMoneroCoreCpp_WASM.wasm`
(both located in `./build` folder), which can
be used in place the files bundled with OpenMonero.
#### Run OpenMonero
Command line options
```bash
./openmonero -h
openmonero, Open Monero backend service:
-h [ --help ] [=arg(=1)] (=0) produce help message
-t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain
-s [ --stagenet ] [=arg(=1)] (=0) use stagenet blockchain
--do-not-relay [=arg(=1)] (=0) does not relay txs to other nodes.
useful when testing construction and
--do-not-relay [=arg(=1)] (=0) does not relay txs to other nodes.
useful when testing construction and
submiting txs
-p [ --port ] arg (=1984) default port for restbed service of
-p [ --port ] arg (=1984) default port for restbed service of
Open Monero
-c [ --config-file ] arg (=./config/config.json)
Config file path.
-m [ --monero-log-level ] arg (=1) Monero log level 1-4, default is 1.
-l [ --log-file ] arg (=./openmonero.log)
Name and path to log file. -l "" to
disable log file.
```
Other backend options are in `confing/config.json`.

@ -1,77 +0,0 @@
#------------------------------------------------------------------------------
# CMake helper for the majority of the cpp-ethereum modules.
#
# This module defines
# Monero_XXX_LIBRARIES, the libraries needed to use ethereum.
# Monero_FOUND, If false, do not try to use ethereum.
#
# File addetped from cpp-ethereum
#
# The documentation for cpp-ethereum is hosted at http://cpp-ethereum.org
#
# ------------------------------------------------------------------------------
# This file is part of cpp-ethereum.
#
# cpp-ethereum is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cpp-ethereum is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2014-2016 cpp-ethereum contributors.
#------------------------------------------------------------------------------
set(LIBS common;blocks;cryptonote_basic;cryptonote_core;
cryptonote_protocol;daemonizer;mnemonics;epee;lmdb;device;
blockchain_db;ringct;wallet;cncrypto;easylogging;version;checkpoints)
set(Xmr_INCLUDE_DIRS "${CPP_MONERO_DIR}")
# if the project is a subset of main cpp-ethereum project
# use same pattern for variables as Boost uses
foreach (l ${LIBS})
string(TOUPPER ${l} L)
find_library(Xmr_${L}_LIBRARY
NAMES ${l}
PATHS ${CMAKE_LIBRARY_PATH}
PATH_SUFFIXES "/src/${l}" "/src/" "/external/db_drivers/lib${l}" "/lib" "/src/crypto" "/contrib/epee/src" "/external/easylogging++/"
NO_DEFAULT_PATH
)
set(Xmr_${L}_LIBRARIES ${Xmr_${L}_LIBRARY})
message(STATUS FindMonero " Xmr_${L}_LIBRARIES ${Xmr_${L}_LIBRARY}")
add_library(${l} STATIC IMPORTED)
set_property(TARGET ${l} PROPERTY IMPORTED_LOCATION ${Xmr_${L}_LIBRARIES})
endforeach()
if (EXISTS ${MONERO_BUILD_DIR}/src/ringct/libringct_basic.a)
message(STATUS FindMonero " found libringct_basic.a")
add_library(ringct_basic STATIC IMPORTED)
set_property(TARGET ringct_basic
PROPERTY IMPORTED_LOCATION ${MONERO_BUILD_DIR}/src/ringct/libringct_basic.a)
endif()
message(STATUS ${MONERO_SOURCE_DIR}/build)
# include monero headers
include_directories(
${MONERO_SOURCE_DIR}/src
${MONERO_SOURCE_DIR}/external
${MONERO_SOURCE_DIR}/build
${MONERO_SOURCE_DIR}/external/easylogging++
${MONERO_SOURCE_DIR}/contrib/epee/include
${MONERO_SOURCE_DIR}/external/db_drivers/liblmdb)

@ -1,96 +0,0 @@
macro(configure_files srcDir destDir)
message(STATUS "Configuring directory ${destDir}")
make_directory(${destDir})
file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*)
foreach(templateFile ${templateFiles})
set(srcTemplatePath ${srcDir}/${templateFile})
if(NOT IS_DIRECTORY ${srcTemplatePath})
message(STATUS "Configuring file ${templateFile}")
configure_file(
${srcTemplatePath}
${destDir}/${templateFile}
@ONLY)
endif(NOT IS_DIRECTORY ${srcTemplatePath})
endforeach(templateFile)
endmacro(configure_files)
macro(create_git_version)
# Get the current working branch
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# http://xit0.org/2013/04/cmake-use-git-branch-and-commit-details-in-project/
# Get the latest abbreviated commit hash of the working branch
execute_process(
COMMAND git log -1 --format=%h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Get the date and time of last commit
execute_process(
COMMAND git log -1 --format=%cd --date=short
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_DATETIME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Get current branch name
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH_NAME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
configure_file(
${CMAKE_SOURCE_DIR}/src/version.h.in
${CMAKE_BINARY_DIR}/gen/version.h
)
include_directories(${CMAKE_BINARY_DIR}/gen)
endmacro(create_git_version)
macro(resource_dir srcDir)
# Scan through resource folder for updated files and copy if none existing or changed
file (GLOB_RECURSE resources "${srcDir}/*.*")
foreach(resource ${resources})
get_filename_component(filename ${resource} NAME)
get_filename_component(dir ${resource} DIRECTORY)
get_filename_component(dirname ${dir} NAME)
set(topdir ${dirname})
set (output "")
while(NOT ${dirname} STREQUAL ${srcDir})
get_filename_component(path_component ${dir} NAME)
set (output "${path_component}/${output}")
get_filename_component(dir ${dir} DIRECTORY)
get_filename_component(dirname ${dir} NAME)
endwhile()
set(output "${CMAKE_CURRENT_BINARY_DIR}/${topdir}/${filename}")
add_custom_command(
COMMENT "Moving updated resource-file '${filename}' to ${output}"
OUTPUT ${output}
DEPENDS ${resource}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${resource}
${output}
)
add_custom_target(${filename} ALL DEPENDS ${resource} ${output})
endforeach()
endmacro(resource_dir)

@ -34,7 +34,7 @@
"wallet_import" :
{
"_comment": "if fee is 0, then importing is free. fee is in base 1e12, e.g., 0.1 xmr is 0.1 x 1e12 = 100000000000",
"fee" : 0,
"fee" : 1000000000,
"testnet" :
{
"address" : "9tzmPMTViHYM3z6NAgQni1Qm1Emzxy5hQFibPgWD3LVTAz91yok5Eni1pH6zKhBHzpTU15GZooPHSGHXFvFuXEdmEG2sWAZ",
@ -56,7 +56,9 @@
"blocks_search_lookahead" : 200,
"search_thread_life_in_seconds" : 120,
"max_number_of_blocks_to_import" : 132000,
"mysql_ping_every_seconds" : 300,
"mysql_ping_every_seconds" : 200,
"_comment": "if the threadpool_size (no of threads) below is 0, its size is automaticly set based on your cpu. If its not 0, the value specified is used instead",
"blockchain_treadpool_size" : 1,
"ssl" :
{
"enable" : false,

@ -1,18 +1,7 @@
cmake_minimum_required(VERSION 3.2)
# now build myext library from other files
project(myext)
# first build mstch template library
add_subdirectory("restbed")
set(SOURCE_FILES
format.cc)
# make static library called libmyxrm
# that we are going to link to
# in the root CMakeLists.txt file
add_library(myext
STATIC
${SOURCE_FILES})

@ -0,0 +1,254 @@
/**
* The ThreadPool class.
* Keeps a set of threads constantly waiting to execute incoming jobs.
* source: http://roar11.com/2016/01/a-platform-independent-thread-pool-using-c14/
*/
#pragma once
#ifndef THREADPOOL_HPP
#define THREADPOOL_HPP
#include "ThreadSafeQueue.hpp"
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <functional>
#include <future>
#include <memory>
#include <thread>
#include <type_traits>
#include <utility>
#include <vector>
namespace TP
{
class ThreadPool
{
private:
class IThreadTask
{
public:
IThreadTask(void) = default;
virtual ~IThreadTask(void) = default;
IThreadTask(const IThreadTask& rhs) = delete;
IThreadTask& operator=(const IThreadTask& rhs) = delete;
IThreadTask(IThreadTask&& other) = default;
IThreadTask& operator=(IThreadTask&& other) = default;
/**
* Run the task.
*/
virtual void execute() = 0;
};
template <typename Func>
class ThreadTask: public IThreadTask
{
public:
ThreadTask(Func&& func)
:m_func{std::move(func)}
{
}
~ThreadTask(void) override = default;
ThreadTask(const ThreadTask& rhs) = delete;
ThreadTask& operator=(const ThreadTask& rhs) = delete;
ThreadTask(ThreadTask&& other) = default;
ThreadTask& operator=(ThreadTask&& other) = default;
/**
* Run the task.
*/
void execute() override
{
m_func();
}
private:
Func m_func;
};
public:
/**
* A wrapper around a std::future that adds the behavior of futures returned from
std::async.
* Specifically, this object will block and wait for execution to finish before going out
of scope.
*/
template <typename T>
class TaskFuture
{
public:
TaskFuture(std::future<T>&& future)
:m_future{std::move(future)}
{
}
TaskFuture(const TaskFuture& rhs) = delete;
TaskFuture& operator=(const TaskFuture& rhs) = delete;
TaskFuture(TaskFuture&& other) = default;
TaskFuture& operator=(TaskFuture&& other) = default;
~TaskFuture(void)
{
if(m_future.valid())
{
m_future.get();
}
}
auto get(void)
{
return m_future.get();
}
private:
std::future<T> m_future;
};
public:
/**
* Constructor.
*/
ThreadPool(void)
:ThreadPool{std::max(std::thread::hardware_concurrency()/2, 2u) - 1u}
{
/*
* Always create at least one thread. If hardware_concurrency() returns 0,
* subtracting one would turn it to UINT_MAX, so get the maximum of
* hardware_concurrency() and 2 before subtracting 1.
*/
}
/**
* Constructor.
*/
explicit ThreadPool(const std::uint32_t numThreads)
:m_done{false},
m_workQueue{},
m_threads{}
{
try
{
for(std::uint32_t i = 0u; i < numThreads; ++i)
{
m_threads.emplace_back(&ThreadPool::worker, this);
}
}
catch(...)
{
destroy();
throw;
}
}
/**
* Non-copyable.
*/
ThreadPool(const ThreadPool& rhs) = delete;
/**
* Non-assignable.
*/
ThreadPool& operator=(const ThreadPool& rhs) = delete;
/**
* Destructor.
*/
~ThreadPool(void)
{
destroy();
}
auto queueSize() const
{
return m_workQueue.size();
}
/**
* Submit a job to be run by the thread pool.
*/
template <typename Func, typename... Args>
auto submit(Func&& func, Args&&... args)
{
auto boundTask = std::bind(std::forward<Func>(func), std::forward<Args>(args)...);
using ResultType = std::result_of_t<decltype(boundTask)()>;
using PackagedTask = std::packaged_task<ResultType()>;
using TaskType = ThreadTask<PackagedTask>;
PackagedTask task{std::move(boundTask)};
TaskFuture<ResultType> result{task.get_future()};
m_workQueue.push(std::make_unique<TaskType>(std::move(task)));
return result;
}
private:
/**
* Constantly running function each thread uses to acquire work items from the queue.
*/
void worker(void)
{
while(!m_done)
{
std::unique_ptr<IThreadTask> pTask{nullptr};
if(m_workQueue.waitPop(pTask))
{
pTask->execute();
}
}
}
/**
* Invalidates the queue and joins all running threads.
*/
void destroy(void)
{
m_done = true;
m_workQueue.invalidate();
for(auto& thread : m_threads)
{
if(thread.joinable())
{
thread.join();
}
}
}
private:
std::atomic_bool m_done;
ThreadSafeQueue<std::unique_ptr<IThreadTask>> m_workQueue;
std::vector<std::thread> m_threads;
};
namespace DefaultThreadPool
{
/**
* Get the default thread pool for the application.
* This pool is created with std::thread::hardware_concurrency() - 1 threads.
*/
inline ThreadPool& getThreadPool(void)
{
static ThreadPool defaultPool;
return defaultPool;
}
inline auto queueSize()
{
return getThreadPool().queueSize();
}
/**
* Submit a job to the default thread pool.
*/
template <typename Func, typename... Args>
inline auto submitJob(Func&& func, Args&&... args)
{
return getThreadPool().submit(
std::forward<Func>(func),
std::forward<Args>(args)...);
}
}
}
#endif

@ -0,0 +1,145 @@
/**
* The ThreadSafeQueue class.
* Provides a wrapper around a basic queue to provide thread safety.
*/
#pragma once
#ifndef THREADSAFEQUEUE_HPP
#define THREADSAFEQUEUE_HPP
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <utility>
namespace TP
{
template <typename T>
class ThreadSafeQueue
{
public:
/**
* Destructor.
*/
~ThreadSafeQueue(void)
{
invalidate();
}
/**
* Attempt to get the first value in the queue.
* Returns true if a value was successfully written to the out parameter, false
otherwise.
*/
bool tryPop(T& out)
{
std::lock_guard<std::mutex> lock{m_mutex};
if(m_queue.empty() || !m_valid)
{
return false;
}
out = std::move(m_queue.front());
m_queue.pop();
return true;
}
/**
* Get the first value in the queue.
* Will block until a value is available unless clear is called or the instance is
destructed.
* Returns true if a value was successfully written to the out parameter, false
otherwise.
*/
bool waitPop(T& out)
{
std::unique_lock<std::mutex> lock{m_mutex};
m_condition.wait(lock, [this]()
{
return !m_queue.empty() || !m_valid;
});
/*
* Using the condition in the predicate ensures that spurious wakeups with a valid
* but empty queue will not proceed, so only need to check for validity before
proceeding.
*/
if(!m_valid)
{
return false;
}
out = std::move(m_queue.front());
m_queue.pop();
return true;
}
/**
* Push a new value onto the queue.
*/
void push(T value)
{
std::lock_guard<std::mutex> lock{m_mutex};
m_queue.push(std::move(value));
m_condition.notify_one();
}
/**
* Check whether or not the queue is empty.
*/
bool empty(void) const
{
std::lock_guard<std::mutex> lock{m_mutex};
return m_queue.empty();
}
/**
* Clear all items from the queue.
*/
void clear(void)
{
std::lock_guard<std::mutex> lock{m_mutex};
while(!m_queue.empty())
{
m_queue.pop();
}
m_condition.notify_all();
}
/**
* Invalidate the queue.
* Used to ensure no conditions are being waited on in waitPop when
* a thread or the application is trying to exit.
* The queue is invalid after calling this method and it is an error
* to continue using a queue after this method has been called.
*/
void invalidate(void)
{
std::lock_guard<std::mutex> lock{m_mutex};
m_valid = false;
m_condition.notify_all();
}
auto size() const
{
std::lock_guard<std::mutex> lock{m_mutex};
return m_queue.size();
}
/**
* Returns whether or not this queue is valid.
*/
bool isValid(void) const
{
std::lock_guard<std::mutex> lock{m_mutex};
return m_valid;
}
private:
std::atomic_bool m_valid{true};
mutable std::mutex m_mutex;
std::queue<T> m_queue;
std::condition_variable m_condition;
};
}
#endif

@ -1,544 +0,0 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
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.
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 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 "format.h"
#include <string.h>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#if defined(_WIN32) && defined(__MINGW32__)
# include <cstring>
#endif
#if FMT_USE_WINDOWS_H
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
#endif
using fmt::internal::Arg;
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
# define FMT_TRY if (true)
# define FMT_CATCH(x) if (false)
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable: 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
return fmt::internal::Null<>();
}
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::Null<>();
}
namespace fmt {
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
namespace {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
# define FMT_SWPRINTF snwprintf
#else
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
const char RESET_COLOR[] = "\x1b[0m";
typedef void (*FormatFunc)(Writer &, int, StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
class StrError {
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
int handle(internal::Null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
ERANGE : result;
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() {
// Suppress a warning about unused strerror_r.
strerror_r(0, FMT_NULL, "");
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(Writer &out, int error_code,
StringRef message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
typedef internal::IntTraits<int>::MainType MainType;
MainType abs_value = static_cast<MainType>(error_code);
if (internal::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::count_digits(abs_value);
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << ERROR_STR << error_code;
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func, int error_code,
StringRef message) FMT_NOEXCEPT {
MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
} // namespace
namespace internal {
// This method is used to preserve binary compatibility with fmt 3.0.
// It can be removed in 4.0.
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
fmt::format_system_error(out, error_code, message);
}
} // namespace internal
FMT_FUNC void SystemError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
template <typename T>
int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, value) :
FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
const char internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \
factor * 10, \
factor * 100, \
factor * 1000, \
factor * 10000, \
factor * 100000, \
factor * 1000000, \
factor * 10000000, \
factor * 100000000, \
factor * 1000000000
template <typename T>
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10
};
FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(FormatError(
format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(FormatError(
format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
}
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
int length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_[length] = 0;
return 0;
}
FMT_FUNC void WindowsError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
FMT_FUNC void internal::format_windows_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
wchar_t *system_message = &buffer[0];
int result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
if (result != 0) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
buffer.resize(internal::INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) {
out << message << ": " << system_message;
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
template <typename Char>
void internal::ArgMap<Char>::init(const ArgList &args) {
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
const NamedArg *named_arg = FMT_NULL;
bool use_values =
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
if (use_values) {
for (unsigned i = 0;/*nothing*/; ++i) {
internal::Arg::Type arg_type = args.type(i);
switch (arg_type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
return;
}
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
internal::Arg::Type arg_type = args.type(i);
if (arg_type == internal::Arg::NAMED_ARG) {
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
}
}
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
switch (args.args_[i].type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
}
template <typename Char>
void internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC Arg internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index];
switch (arg.type) {
case Arg::NONE:
error = "argument index out of range";
break;
case Arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer);
break;
default:
/*nothing*/;
}
return arg;
}
FMT_FUNC void report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
print(format, args);
std::fputs(RESET_COLOR, stdout);
}
#ifndef FMT_HEADER_ONLY
template struct internal::BasicData<void>;
// Explicit instantiations for char.
template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args);
template int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
template int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
} // namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -442,7 +442,14 @@ var cnUtil = (function(initConfig) {
first = seed; //only input reduced seeds or this will not give you the result you want
}
keys.spend = this.generate_keys(first);
var second = this.cn_fast_hash(first);
var second;
if (seed.length !== 64) {
second = this.cn_fast_hash(first);
} else {
second = this.cn_fast_hash(keys.spend.sec);
}
keys.view = this.generate_keys(second);
keys.public_addr = this.pubkeys_to_string(keys.spend.pub, keys.view.pub);
return keys;
@ -462,9 +469,12 @@ var cnUtil = (function(initConfig) {
this.decode_address = function(address) {
var dec = cnBase58.decode(address);
var expectedPrefix = this.encode_varint(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
var expectedPrefixInt = this.encode_varint(CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX);
var expectedPrefixSub = this.encode_varint(CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX);
var prefix = dec.slice(0, expectedPrefix.length);
if (prefix !== expectedPrefix && prefix !== expectedPrefixInt && prefix !== expectedPrefixSub) {
throw "Invalid address prefix";

@ -238,7 +238,7 @@ thinwalletCtrls.controller('AccountCtrl', function($scope, $rootScope, $http, $q
// decrypt payment_id8 which results in using
// integrated address
if (transactions[i].payment_id.length == 16) {
if (transactions[i].payment_id.length === 16) {
if (transactions[i].tx_pub_key) {
var decrypted_payment_id8
= decrypt_payment_id(transactions[i].payment_id,

@ -89,7 +89,10 @@ thinwalletCtrls.controller("ImportWalletCtrl", function($scope, $location, $http
$timeout(function(){ModalService.hide('import-wallet')}, 5000);
}
},function(response) {
$scope.error = "Error connecting to the backend. Can't get payment import data.";
var data = response.data;
$scope.error = data && data.Error
? data.Error
: "Something went wrong getting payment details";
});
}

@ -45,6 +45,7 @@ thinwalletCtrls.controller("LoginCtrl", function($scope, $location, AccountServi
} else if (config.nettype == 2) {
// just some dummy account, as not to fill login form every time.
$scope.mnemonic = "gels lair teeming cease nanny utility inexact leisure civilian emerge zippers skew gasp enjoy fugitive nanny candy nuance muppet scrub uneven yard ulcers unquoted yard";
$scope.mnemonic = "examine wiring jagged rowboat number smelting square pirate gown tiger cactus camp dude economics rage aside fainted bemused toenail skater maps woven voyage nanny cactus"
} else {
$scope.address = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A";
$scope.view_key = "f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501" ;

@ -36,66 +36,23 @@ class HostedMoneroAPIClient
throw "self.$http required"
}
}
//
// Getting outputs for sending funds
UnspentOuts(
address,
view_key__private,
spend_key__public,
spend_key__private,
mixinNumber,
sweeping,
fn
) { // -> RequestHandle
UnspentOuts(parameters, fn)
{ // -> RequestHandle
const self = this
mixinNumber = parseInt(mixinNumber) // jic
//
var parameters =
{
address: address,
view_key: view_key__private
};
parameters.amount = '0'
parameters.mixin = mixinNumber
parameters.use_dust = true // Client now filters unmixable by dustthreshold amount (unless sweeping) + non-rct
parameters.dust_threshold = mymonero_core_js.monero_config.dustThreshold.toString()
const endpointPath = 'get_unspent_outs'
self.$http.post(config.apiUrl + endpointPath, parameters).then(
function(data)
{
__proceedTo_parseAndCallBack(data.data)
fn(null, data.data)
}
).catch(
function(data)
{
fn(data && data.Error ? data.Error : "Something went wrong with getting your available balance for spending");
fn(data && data.data.Error
? data.data.Error
: "Something went wrong with getting your available balance for spending");
}
);
function __proceedTo_parseAndCallBack(data)
{
mymonero_core_js.monero_utils_promise.then(function(monero_utils)
{
mymonero_core_js.api_response_parser_utils.Parsed_UnspentOuts__keyImageManaged(
data,
address,
view_key__private,
spend_key__public,
spend_key__private,
monero_utils,
function(err, retVals)
{
if (err) {
fn(err)
return
}
fn(null, retVals.unspentOutputs, retVals.per_byte_fee__string)
}
)
}).catch(function(err)
{
fn(err)
})
}
const requestHandle =
{
abort: function()
@ -105,51 +62,24 @@ class HostedMoneroAPIClient
}
return requestHandle
}
//
RandomOuts(
using_outs,
mixinNumber,
fn
) { // -> RequestHandle
RandomOuts(parameters, fn)
{ // -> RequestHandle
const self = this
//
mixinNumber = parseInt(mixinNumber)
if (mixinNumber < 0 || isNaN(mixinNumber)) {
const errStr = "Invalid mixin - must be >= 0"
const err = new Error(errStr)
fn(err)
return
}
//
var amounts = [];
for (var l = 0; l < using_outs.length; l++) {
amounts.push(using_outs[l].rct ? "0" : using_outs[l].amount.toString())
}
//
var parameters =
{
amounts: amounts,
count: mixinNumber + 1 // Add one to mixin so we can skip real output key if necessary
}
const endpointPath = 'get_random_outs'
self.$http.post(config.apiUrl + endpointPath, parameters).then(
function(data)
{
__proceedTo_parseAndCallBack(data.data)
fn(null, data.data)
}
).catch(
function(data)
{
fn(data && data.Error ? data.Error : "Something went wrong while getting decoy outputs");
fn(data
&& data.data.Error
? data.data.Error
: "Something went wrong while getting decoy outputs");
}
);
function __proceedTo_parseAndCallBack(data)
{
console.log("debug: info: random outs: data", data)
const amount_outs = data.amount_outs
//
fn(null, amount_outs)
}
const requestHandle =
{
abort: function()
@ -159,46 +89,24 @@ class HostedMoneroAPIClient
}
return requestHandle
}
//
// Runtime - Imperatives - Public - Sending funds
SubmitSerializedSignedTransaction(
address,
view_key__private,
serializedSignedTx,
fn // (err?) -> RequestHandle
) {
SubmitRawTx(parameters, fn)
{
const self = this
//
var parameters =
{
address: address,
view_key: view_key__private
};
parameters.tx = serializedSignedTx
const endpointPath = 'submit_raw_tx'
self.$http.post(config.apiUrl + endpointPath, parameters).then(
function(data)
{
if (data.data.error)
{
const errStr = "Invalid mixin - must be >= 0";
const err = new Error(data.data.error);
fn(err);
return;
}
__proceedTo_parseAndCallBack(data.data)
fn(null, data.data)
}
).catch(
function(data)
{
fn(data && data.Error ? data.Error : "Something went wrong with getting your available balance for spending");
//console.log("submit_raw_data_error:", data);
fn(data && data.data.Error
? data.data.Error
: "Something went wrong while submitting your transaction");
}
);
function __proceedTo_parseAndCallBack(data)
{
console.log("debug: info: submit_raw_tx: data", data)
fn(null)
}
const requestHandle =
{
abort: function()
@ -282,7 +190,7 @@ thinwalletCtrls.controller('SendCoinsCtrl', function($scope, $http, $q, AccountS
$scope.error = "";
$scope.submitting = true;
//
mymonero_core_js.monero_utils_promise.then(function(monero_utils)
mymonero_core_js.monero_utils_promise.then(function(coreBridge_instance)
{
if (targets.length > 1) {
throw "MyMonero currently only supports one target"
@ -296,7 +204,7 @@ thinwalletCtrls.controller('SendCoinsCtrl', function($scope, $http, $q, AccountS
if (err) {
console.error("Err:", err)
$scope.status = "";
$scope.error = "Something unexpected occurred when submitting your transaction: " + (err.Error || err);
$scope.error = "Something unexpected occurred when sending funds: " + (err.Error || err);
$scope.submitting = false;
return
}
@ -327,6 +235,7 @@ thinwalletCtrls.controller('SendCoinsCtrl', function($scope, $http, $q, AccountS
$http.post(config.apiUrl + "get_txt_records", {
domain: domain
}).then(function(data) {
var data = data.data;
var records = data.records;
var oaRecords = [];
console.log(domain + ": ", data.records);
@ -359,7 +268,7 @@ thinwalletCtrls.controller('SendCoinsCtrl', function($scope, $http, $q, AccountS
console.log("OpenAlias record: ", oaRecords[0]);
var oaAddress = oaRecords[0].address;
try {
monero_utils.decode_address(oaAddress, config.nettype);
coreBridge_instance.decode_address(oaAddress, config.nettype);
confirmOpenAliasAddress(
domain,
oaAddress,
@ -390,12 +299,12 @@ thinwalletCtrls.controller('SendCoinsCtrl', function($scope, $http, $q, AccountS
// xmr address (incl subaddresses):
try {
// verify that the address is valid
monero_utils.decode_address(target.address, config.nettype);
sendTo(target.address, amount, null /*domain*/);
coreBridge_instance.decode_address(target.address, config.nettype);
} catch (e) {
fn("Failed to decode address (#" + i + "): " + e);
fn("Failed to decode address with error: " + e);
return;
}
sendTo(target.address, amount, null /*domain*/);
//
function sendTo(target_address, amount, domain/*may be null*/)
{
@ -409,40 +318,76 @@ thinwalletCtrls.controller('SendCoinsCtrl', function($scope, $http, $q, AccountS
//
_configureWith_statusUpdate(statusUpdate_messageBase);
//
const monero_sendingFunds_utils = mymonero_core_js.monero_sendingFunds_utils
monero_sendingFunds_utils.SendFunds(
target_address,
config.nettype,
amount,
sweeping,
AccountService.getAddress(),
AccountService.getSecretKeys(),
AccountService.getPublicKeys(),
new HostedMoneroAPIClient({ $http: $http }),
payment_id, // passed in
1, /* priority */
function(code)
const sec_keys = AccountService.getSecretKeys()
const pub_keys = AccountService.getPublicKeys()
const apiClient = new HostedMoneroAPIClient({ $http: $http })
var parsed_amount;
try {
parsed_amount = mymonero_core_js.monero_amount_format_utils.parseMoney(target.amount);
} catch (e) {
fn("Please enter a valid amount");
return
}
const send_args =
{
is_sweeping: sweeping,
payment_id_string: payment_id, // passed in
sending_amount: sweeping ? 0 : parsed_amount.toString(), // sending amount
from_address_string: AccountService.getAddress(),
sec_viewKey_string: sec_keys.view,
sec_spendKey_string: sec_keys.spend,
pub_spendKey_string: pub_keys.spend,
to_address_string: target_address,
priority: 1,
unlock_time: 0, // unlock_time
nettype: config.nettype,
//
get_unspent_outs_fn: function(req_params, cb)
{
apiClient.UnspentOuts(req_params, function(err_msg, res)
{
cb(err_msg, res);
});
},
get_random_outs_fn: function(req_params, cb)
{
let suffix = monero_sendingFunds_utils.SendFunds_ProcessStep_MessageSuffix[code]
apiClient.RandomOuts(req_params, function(err_msg, res)
{
cb(err_msg, res);
});
},
submit_raw_tx_fn: function(req_params, cb)
{
apiClient.SubmitRawTx(req_params, function(err_msg, res)
{
cb(err_msg, res);
});
},
//
status_update_fn: function(params)
{
let suffix = mymonero_core_js.monero_sendingFunds_utils.SendFunds_ProcessStep_MessageSuffix[params.code]
_configureWith_statusUpdate(
statusUpdate_messageBase + " " + suffix, // TODO: localize this concatenation
code
params.code
)
},
function(
currencyReady_targetDescription_address,
sentAmount, final__payment_id,
tx_hash, tx_fee, tx_key, mixin
) {
let total_sent__JSBigInt = sentAmount.add(tx_fee)
let total_sent__atomicUnitString = total_sent__JSBigInt.toString()
let total_sent__floatString = mymonero_core_js.monero_amount_format_utils.formatMoney(total_sent__JSBigInt)
let total_sent__float = parseFloat(total_sent__floatString)
error_fn: function(params)
{
fn(params.err_msg)
},
success_fn: function(params)
{
const total_sent__JSBigInt = new JSBigInt(params.total_sent)
const tx_fee = new JSBigInt(params.used_fee)
const total_sent__atomicUnitString = total_sent__JSBigInt.toString()
const total_sent__floatString = mymonero_core_js.monero_amount_format_utils.formatMoney(total_sent__JSBigInt)
const total_sent__float = parseFloat(total_sent__floatString)
//
const mockedTransaction =
{
hash: tx_hash,
mixin: "" + mixin,
hash: params.tx_hash,
mixin: "" + params.mixin,
coinbase: false,
mempool: true, // is that correct?
//
@ -459,20 +404,24 @@ thinwalletCtrls.controller('SendCoinsCtrl', function($scope, $http, $q, AccountS
approx_float_amount: -1 * total_sent__float, // -1 cause it's outgoing
// amount: new JSBigInt(sentAmount), // not really used (note if you uncomment, import JSBigInt)
//
payment_id: final__payment_id, // b/c `payment_id` may be nil of short pid was used to fabricate an integrated address
payment_id: params.final_payment_id, // b/c `payment_id` may be nil of short pid was used to fabricate an integrated address
//
// info we can only preserve locally
tx_fee: tx_fee,
tx_key: tx_key,
tx_key: params.tx_key,
tx_pub_key: params.tx_pub_key,
target_address: target_address, // only we here are saying it's the target
};
fn(null, mockedTransaction, domain)
},
function(err)
{ // failed-fn
fn(err)
}
)
}
try {
// verify that the address is valid
coreBridge_instance.async__send_funds(send_args);
} catch (e) {
fn("Failed to send with exception: " + e);
return;
}
}
})
};

@ -28,6 +28,8 @@ thinwalletCtrls.controller('TransactionDetailsCtrl', function ($scope,
$scope.tx_age = "";
$scope.payment_id = "";
$scope.tx_amount = "";
$scope.tx_version = "";
$scope.rct_type = "";
$scope.fetching = true;
@ -85,6 +87,8 @@ thinwalletCtrls.controller('TransactionDetailsCtrl', function ($scope,
$scope.timestamp = new Date(data.timestamp);
$scope.no_outputs = data.num_of_outputs;
$scope.no_inputs = data.num_of_inputs;
$scope.tx_version = data.tx_version;
$scope.rct_type = data.rct_type;
var age_duration = moment.duration(new Date() - new Date(data.timestamp));

File diff suppressed because one or more lines are too long

@ -76,18 +76,25 @@ thinwalletServices
} catch (e) {
return deferred.reject("invalid address");
}
var expected_view_pub = cnUtil.sec_key_to_pub(view_key);
var expected_spend_pub;
if (spend_key.length === 64) {
expected_spend_pub = cnUtil.sec_key_to_pub(spend_key);
}
if (public_keys.view !== expected_view_pub) {
accountService.logout();
return deferred.reject("invalid view key");
}
if (!view_only && (public_keys.spend !== expected_spend_pub)) {
accountService.logout();
return deferred.reject("invalid spend key");
if (!cnUtil.is_subaddress(address))
{
var expected_view_pub = cnUtil.sec_key_to_pub(view_key);
var expected_spend_pub;
if (spend_key.length === 64) {
expected_spend_pub = cnUtil.sec_key_to_pub(spend_key);
}
if (public_keys.view !== expected_view_pub) {
accountService.logout();
return deferred.reject("invalid view key");
}
if (!view_only && (public_keys.spend !== expected_spend_pub)) {
accountService.logout();
return deferred.reject("invalid spend key");
}
}
public_address = address;
private_keys = {

@ -53,10 +53,14 @@
<label class="field-label review">Is coinbase?</label>
<div class="review-text">{{coinbase}}</div>
</div>
<div class="w-col w-col-10 responsive-column">
<div class="w-col w-col-7 responsive-column">
<label class="field-label review" for="Mnemonic-2">Payment ID</label>
<div class="review-text">{{payment_id || "N/A"}}</div>
</div>
<div class="w-col w-col-3 responsive-column">
<label class="field-label review" for="Mnemonic-2">tx/rct type</label>
<div class="review-text">{{tx_version}}/{{rct_type}}</div>
</div>
</div>
<div class="w-row">
<div class="w-col w-col-12 responsive-column">

@ -104,11 +104,11 @@
<div class="move-text-div">
<div class="middle-text receive">{{sent_tx.tx_id}}</div>
</div>
<!--
<label class="send-label" >Transaction Private Key</label>
<label class="send-label" >Transaction Private Key (save it as the key is not recoverable!)</label>
<div class="move-text-div">
<div class="middle-text receive">{{sent_tx.tx_prvkey}}</div>
<div class="middle-text receive">{{sent_tx.tx_key}}</div>
</div>
<!--
<label class="send-label" >Soon should be visible in a blockchain explorer</label>
<div class="move-text-div">
<a class="login-link" href="{{ sent_tx.explorerLink }} " target="_blank">{{ sent_tx.explorerLink }}</a>

@ -1,9 +1,9 @@
#include "src/om_log.h"
#include "src/CmdLineOptions.h"
#include "src/MicroCore.h"
#include "src/YourMoneroRequests.h"
#include "src/OpenMoneroRequests.h"
#include "src/ThreadRAII.h"
#include "src/MysqlPing.h"
#include "src/db/MysqlPing.h"
#include <iostream>
#include <memory>
@ -15,6 +15,33 @@ using namespace restbed;
using boost::filesystem::path;
// signal exit handler, addpated from aleth
class ExitHandler
{
public:
static std::mutex m;
static std::condition_variable cv;
static void exitHandler(int)
{
std::lock_guard<std::mutex> lk(m);
s_shouldExit = true;
OMINFO << "Request to finish the openmonero received";
cv.notify_one();
}
bool shouldExit() const { return s_shouldExit; }
private:
static bool s_shouldExit;
};
bool ExitHandler::s_shouldExit {false};
std::mutex ExitHandler::m;
std::condition_variable ExitHandler::cv;
int
main(int ac, const char* av[])
{
@ -30,11 +57,20 @@ if (*help_opt)
return EXIT_SUCCESS;
}
auto monero_log_level =
*(opts.get_option<size_t>("monero-log-level"));
if (monero_log_level < 1 || monero_log_level > 4)
{
cerr << "monero-log-level,m option must be between 1 and 4!\n";
return EXIT_SUCCESS;
}
// setup monero logger
mlog_configure(mlog_get_default_log_path(""), true);
mlog_set_log("1");
mlog_set_log(std::to_string(monero_log_level).c_str());
string log_file = *(opts.get_option<string>("log-file"));
auto log_file = *(opts.get_option<string>("log-file"));
// setup a logger for Open Monero
@ -51,7 +87,13 @@ if (!log_file.empty())
defaultConf.setGlobally(el::ConfigurationType::ToStandardOutput, "true");
el::Loggers::reconfigureLogger("openmonero", defaultConf);
// default format: %datetime %level [%logger] %msg
// we change to add file and func
defaultConf.setGlobally(el::ConfigurationType::Format,
"%datetime [%levshort,%logger,%fbase:%func:%line]"
" %msg");
el::Loggers::reconfigureLogger(OPENMONERO_LOG_CATEGORY, defaultConf);
OMINFO << "OpenMonero is starting";
@ -88,7 +130,7 @@ nlohmann::json config_json = bc_setup.get_config();
//cast port number in string to uint16
uint16_t app_port = boost::lexical_cast<uint16_t>(*port_opt);
auto app_port = boost::lexical_cast<uint16_t>(*port_opt);
// set mysql/mariadb connection details
xmreg::MySqlConnector::url = config_json["database"]["url"];
@ -97,6 +139,22 @@ xmreg::MySqlConnector::username = config_json["database"]["user"];
xmreg::MySqlConnector::password = config_json["database"]["password"];
xmreg::MySqlConnector::dbname = config_json["database"]["dbname"];
// number of thread in blockchain access pool thread
auto threads_no = std::max<uint32_t>(
std::thread::hardware_concurrency()/2, 2u) - 1;
if (bc_setup.blockchain_treadpool_size > 0)
threads_no = bc_setup.blockchain_treadpool_size;
if (threads_no > 100)
{
threads_no = 100;
OMWARN << "Requested Thread Pool size "
<< threads_no << " is greater than 100!."
" Overwriting to 100!" ;
}
OMINFO << "Thread pool size: " << threads_no << " threads";
// once we have all the parameters for the blockchain and our backend
// we can create and instance of CurrentBlockchainStatus class.
@ -108,7 +166,8 @@ auto current_bc_status
= make_shared<xmreg::CurrentBlockchainStatus>(
bc_setup,
std::make_unique<xmreg::MicroCore>(),
std::make_unique<xmreg::RPCCalls>(bc_setup.deamon_url));
std::make_unique<xmreg::RPCCalls>(bc_setup.deamon_url),
std::make_unique<TP::ThreadPool>(threads_no));
// since CurrentBlockchainStatus class monitors current status
// of the blockchain (e.g., current height) .This is the only class
@ -125,10 +184,11 @@ if (!current_bc_status->init_monero_blockchain())
// by tx searching threads that are launched for each user independently,
// when they log back or create new account.
xmreg::ThreadRAII blockchain_monitoring_thread(
std::thread([current_bc_status]
{current_bc_status->monitor_blockchain();}),
xmreg::ThreadRAII::DtorAction::join);
std::thread blockchain_monitoring_thread(
[&current_bc_status]()
{
current_bc_status->monitor_blockchain();
});
OMINFO << "Blockchain monitoring thread started";
@ -169,16 +229,19 @@ catch(std::exception const& e)
xmreg::MysqlPing mysql_ping {
mysql_accounts->get_connection(),
bc_setup.mysql_ping_every_seconds};
bc_setup.mysql_ping_every};
std::thread mysql_ping_thread(
[&mysql_ping]()
{
mysql_ping();
});
xmreg::ThreadRAII mysql_ping_thread(
std::thread(std::ref(mysql_ping)),
xmreg::ThreadRAII::DtorAction::join);
OMINFO << "MySQL ping thread started";
// create REST JSON API services
xmreg::YourMoneroRequests open_monero(mysql_accounts, current_bc_status);
xmreg::OpenMoneroRequests open_monero(mysql_accounts, current_bc_status);
// create Open Monero APIs
MAKE_RESOURCE(login);
@ -241,8 +304,55 @@ else
OMINFO << "Start the service at http://127.0.0.1:" << app_port;
}
// intercept basic termination requests,
// including Ctrl+c
ExitHandler exitHandler;
signal(SIGABRT, ExitHandler::exitHandler);
signal(SIGTERM, ExitHandler::exitHandler);
signal(SIGINT, ExitHandler::exitHandler);
// main restbed thread. this is where
// restbed will be running and handling
// requests
std::thread restbed_service(
[&service, &settings]()
{
OMINFO << "Starting restbed service thread.";
service.start(settings);
});
// we are going to whait here for as long
// as control+c hasn't been pressed
{
std::unique_lock<std::mutex> lk(ExitHandler::m);
ExitHandler::cv.wait(lk, [&exitHandler]{
return exitHandler.shouldExit();});
}
//////////////////////////////////////////////
// Try to gracefully stop all threads/services
//////////////////////////////////////////////
OMINFO << "Stopping restbed service.";
service.stop();
restbed_service.join();
OMINFO << "Stopping blockchain_monitoring_thread. Please wait.";
current_bc_status->stop();
blockchain_monitoring_thread.join();
OMINFO << "Stopping mysql_ping. Please wait.";
mysql_ping.stop();
mysql_ping_thread.join();
OMINFO << "Disconnecting from database.";
mysql_accounts->disconnect();
service.start(settings);
OMINFO << "All done. Bye.";
return EXIT_SUCCESS;
}

@ -109,7 +109,7 @@ CREATE TABLE IF NOT EXISTS `Payments` (
`tx_hash` varchar(64) NOT NULL DEFAULT '',
`request_fulfilled` tinyint(1) NOT NULL DEFAULT '0',
`import_fee` bigint(20) NOT NULL,
`payment_address` varchar(95) NOT NULL,
`payment_address` varchar(106) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),

@ -274,7 +274,7 @@ CREATE TABLE IF NOT EXISTS `Payments` (
`tx_hash` varchar(64) NOT NULL DEFAULT '',
`request_fulfilled` tinyint(1) NOT NULL DEFAULT '0',
`import_fee` bigint(20) NOT NULL,
`payment_address` varchar(95) NOT NULL,
`payment_address` varchar(106) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),

@ -158,18 +158,20 @@ BlockchainSetup::read_config(string config_json_path)
void
BlockchainSetup::_init()
{
refresh_block_status_every_seconds
= config_json["refresh_block_status_every_seconds"];
refresh_block_status_every
= seconds {config_json["refresh_block_status_every_seconds"]};
blocks_search_lookahead
= config_json["blocks_search_lookahead"];
max_number_of_blocks_to_import
= config_json["max_number_of_blocks_to_import"];
search_thread_life_in_seconds
= config_json["search_thread_life_in_seconds"];
search_thread_life
= seconds {config_json["search_thread_life_in_seconds"]};
import_fee
= config_json["wallet_import"]["fee"];
mysql_ping_every_seconds
= config_json["mysql_ping_every_seconds"];
mysql_ping_every
= seconds {config_json["mysql_ping_every_seconds"]};
blockchain_treadpool_size
= config_json["blockchain_treadpool_size"];
get_blockchain_path();

@ -5,8 +5,8 @@
#ifndef OPENMONERO_BLOCKCHAINSETUP_H
#define OPENMONERO_BLOCKCHAINSETUP_H
#include "monero_headers.h"
#include "tools.h"
#include "src/monero_headers.h"
#include "utils.h"
#include <string>
@ -17,6 +17,8 @@ using namespace crypto;
using namespace cryptonote;
using namespace std;
using chrono::seconds;
class BlockchainSetup
{
public:
@ -37,25 +39,25 @@ public:
network_type net_type;
bool do_not_relay;
uint64_t refresh_block_status_every_seconds;
bool do_not_relay {false};
uint64_t blocks_search_lookahead;
seconds refresh_block_status_every {10};
seconds search_thread_life {120};
seconds mysql_ping_every {300};
uint64_t max_number_of_blocks_to_import;
uint64_t blocks_search_lookahead {200};
uint64_t search_thread_life_in_seconds;
uint64_t max_number_of_blocks_to_import {132000};
uint64_t mysql_ping_every_seconds;
uint64_t blockchain_treadpool_size {0};
string import_payment_address_str;
string import_payment_viewkey_str;
uint64_t import_fee;
uint64_t import_fee {0};
uint64_t spendable_age;
uint64_t spendable_age_coinbase;
uint64_t spendable_age {10};
uint64_t spendable_age_coinbase {60};
address_parse_info import_payment_address;
secret_key import_payment_viewkey;

@ -3,23 +3,22 @@ cmake_minimum_required(VERSION 3.2)
project(myxrm)
set(SOURCE_FILES
MicroCore.cpp
tools.cpp
CmdLineOptions.cpp
CurrentBlockchainStatus.cpp
MySqlConnector.cpp
MySqlAccounts.cpp
ssqlses.cpp
YourMoneroRequests.cpp
db/MySqlConnector.cpp
db/MySqlAccounts.cpp
db/ssqlses.cpp
OpenMoneroRequests.cpp
TxSearch.cpp
RPCCalls.cpp
OutputInputIdentification.cpp
version.h.in
RPCCalls.cpp
omversion.h.in
BlockchainSetup.cpp
ThreadRAII.cpp
MysqlPing.cpp
TxUnlockChecker.cpp
RandomOutputs.cpp)
db/MysqlPing.cpp
TxUnlockChecker.cpp
utils.cpp
RandomOutputs.cpp)
# make static library called libmyxrm
# that we are going to link to
@ -27,3 +26,12 @@ set(SOURCE_FILES
add_library(myxrm
STATIC
${SOURCE_FILES})
target_include_monero_directories(myxrm)
target_include_directories(myxrm PRIVATE "xmregcore")
#target_include_directories(myxmr
# PUBLIC
#${MONERO_SOURCE_DIR}/contrib/epee/include)

@ -37,6 +37,9 @@ namespace xmreg
("config-file,c", value<string>()
->default_value("./config/config.json"),
"Config file path.")
("monero-log-level,m", value<size_t>()
->default_value(1),
"Monero log level 1-4, default is 1.")
("log-file,l", value<string>()
->default_value("./openmonero.log"),
"Name and path to log file. -l \"\" to disable log file.");

File diff suppressed because it is too large Load Diff

@ -3,17 +3,19 @@
#define MYSQLPP_SSQLS_NO_STATICS 1
#include "om_log.h"
#include "MicroCore.h"
#include "ssqlses.h"
#include "src/MicroCore.h"
#include "db/ssqlses.h"
#include "TxUnlockChecker.h"
#include "BlockchainSetup.h"
#include "TxSearch.h"
#include "tools.h"
#include "utils.h"
#include "ThreadRAII.h"
#include "RPCCalls.h"
#include "MySqlAccounts.h"
#include "db/MySqlAccounts.h"
#include "RandomOutputs.h"
#include "../ext/ThreadPool.hpp"
#include <iostream>
#include <memory>
#include <thread>
@ -27,7 +29,7 @@ using namespace std;
class XmrAccount;
class MySqlAccounts;
class TxSearch;
/*
* This is a thread class. Probably it should be singleton, as we want
@ -60,13 +62,17 @@ public:
CurrentBlockchainStatus(BlockchainSetup _bc_setup,
std::unique_ptr<MicroCore> _mcore,
std::unique_ptr<RPCCalls> _rpc);
std::unique_ptr<RPCCalls> _rpc,
std::unique_ptr<TP::ThreadPool> _tp);
virtual void
monitor_blockchain();
virtual uint64_t
get_current_blockchain_height();
virtual uint64_t
get_hard_fork_version() const;
virtual void
update_current_blockchain_height();
@ -113,6 +119,16 @@ public:
get_tx_with_output(uint64_t output_idx, uint64_t amount,
transaction& tx, uint64_t& output_idx_in_tx);
virtual tx_out_index
get_output_tx_and_index(uint64_t amount,
uint64_t index) const;
virtual void
get_output_tx_and_index(
uint64_t amount,
std::vector<uint64_t> const& offsets,
std::vector<tx_out_index>& indices) const;
virtual bool
get_output_keys(const uint64_t& amount,
const vector<uint64_t>& absolute_offsets,
@ -141,6 +157,15 @@ public:
RandomOutputs::outs_for_amount_v&
found_outputs);
virtual bool
get_output_histogram(
COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req,
COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res) const;
virtual bool
get_outs(COMMAND_RPC_GET_OUTPUTS_BIN::request const& req,
COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const;
virtual uint64_t
get_dynamic_per_kb_fee_estimate() const;
@ -211,7 +236,8 @@ public:
get_tx(string const& tx_hash_str, transaction& tx);
virtual bool
get_tx_block_height(crypto::hash const& tx_hash, int64_t& tx_height);
get_tx_block_height(crypto::hash const& tx_hash,
int64_t& tx_height);
virtual bool
set_new_searched_blk_no(const string& address,
@ -223,11 +249,15 @@ public:
virtual bool
get_known_outputs_keys(string const& address,
unordered_map<public_key, uint64_t>& known_outputs_keys);
unordered_map<public_key,
uint64_t>& known_outputs_keys);
virtual void
clean_search_thread_map();
virtual void
stop_search_threads();
/*
* The frontend requires rct field to work
* the filed consisitct of rct_pk, mask, and amount.
@ -264,6 +294,9 @@ public:
virtual TxSearch&
get_search_thread(string const& acc_address);
inline virtual void
stop() {stop_blockchain_monitor_loop = true;}
// default destructor is fine
virtual ~CurrentBlockchainStatus() = default;
@ -291,6 +324,13 @@ protected:
// this class is also the only class which can
// use talk to monero deamon using RPC.
std::unique_ptr<RPCCalls> rpc;
// any operation required to use blockchain
// i.e., access through mcore, will be performed
// by threads in this thread_pool. we have to
// have fixed and limited number of threads so that
// the lmdb does not throw MDB_READERS_FULL
std::unique_ptr<TP::ThreadPool> thread_pool;
// vector of mempool transactions that all threads
// can refer to
@ -312,6 +352,7 @@ protected:
// to synchronize access to mempool_txs vector
mutex getting_mempool_txs;
// have this method will make it easier to moc
// RandomOutputs in our tests later
virtual unique_ptr<RandomOutputs>
@ -321,5 +362,38 @@ protected:
};
// small adapter class that will anable using
// BlockchainCurrentStatus inside UniversalAdapter
// for locating inputs. We do this becasuse
// BlockchainCurrentStatus is using a thread pool
// to access MicroCore and blockchain. So we don't want
// to miss on that. Also UnversalAdapter for Inputs
// takes AbstractCore interface
class MicroCoreAdapter : public AbstractCore
{
public:
MicroCoreAdapter(CurrentBlockchainStatus* _cbs);
virtual void
get_output_key(uint64_t amount,
vector<uint64_t> const& absolute_offsets,
vector<cryptonote::output_data_t>& outputs)
/*const*/ override;
virtual void
get_output_tx_and_index(
uint64_t amount,
std::vector<uint64_t> const& offsets,
std::vector<tx_out_index>& indices)
const override;
virtual bool
get_tx(crypto::hash const& tx_hash, transaction& tx)
const override;
private:
CurrentBlockchainStatus* cbs {};
};
}

@ -1,241 +0,0 @@
//
// Created by mwo on 5/11/15.
//
#include "MicroCore.h"
namespace xmreg
{
/**
* The constructor is interesting, as
* m_mempool and m_blockchain_storage depend
* on each other.
*
* So basically m_mempool is initialized with
* reference to Blockchain (i.e., Blockchain&)
* and m_blockchain_storage is initialized with
* reference to m_mempool (i.e., tx_memory_pool&)
*
* The same is done in cryptonode::core.
*/
MicroCore::MicroCore():
m_mempool(core_storage),
core_storage(m_mempool),
m_device {&hw::get_device("default")}
{
}
/**
* Initialized the MicroCore object.
*
* Create BlockchainLMDB on the heap.
* Open database files located in blockchain_path.
* Initialize m_blockchain_storage with the BlockchainLMDB object.
*/
bool
MicroCore::init(const string& _blockchain_path, network_type nt)
{
blockchain_path = _blockchain_path;
nettype = nt;
std::unique_ptr<BlockchainDB> db = std::make_unique<BlockchainLMDB>();
try
{
// try opening lmdb database files
db->open(blockchain_path, DBF_RDONLY);
}
catch (const std::exception& e)
{
cerr << "Error opening database: " << e.what();
return false;
}
// check if the blockchain database
// is successful opened
if(!db->is_open())
return false;
// initialize Blockchain object to manage
// the database.
if (!core_storage.init(db.release(), nettype))
{
cerr << "Error opening database: core_storage->init(db, nettype)\n" ;
return false;
}
if (!m_mempool.init())
{
cerr << "Error opening database: m_mempool.init()\n" ;
return false;
}
initialization_succeded = true;
return true;
}
/**
* Get m_blockchain_storage.
* Initialize m_blockchain_storage with the BlockchainLMDB object.
*/
Blockchain const&
MicroCore::get_core() const
{
return core_storage;
}
tx_memory_pool const&
MicroCore::get_mempool() const
{
return m_mempool;
}
bool
MicroCore::get_block_from_height(uint64_t height, block& blk) const
{
try
{
blk = core_storage.get_db().get_block_from_height(height);
}
catch (const exception& e)
{
cerr << e.what() << endl;
return false;
}
return true;
}
bool
MicroCore::get_block_complete_entry(block const& b, block_complete_entry& bce)
{
bce.block = cryptonote::block_to_blob(b);
for (const auto &tx_hash: b.tx_hashes)
{
transaction tx;
if (!get_tx(tx_hash, tx))
return false;
cryptonote::blobdata txblob = tx_to_blob(tx);
bce.txs.push_back(txblob);
}
return true;
}
bool
MicroCore::get_tx(crypto::hash const& tx_hash, transaction& tx) const
{
if (core_storage.have_tx(tx_hash))
{
// get transaction with given hash
tx = core_storage.get_db().get_tx(tx_hash);
return true;
}
return true;
}
bool
MicroCore::get_output_histogram(
vector<uint64_t> const& amounts,
uint64_t min_count,
histogram_map& histogram,
bool unlocked,
uint64_t recent_cutoff) const
{
try
{
histogram = core_storage.get_output_histogram(
amounts,
unlocked,
recent_cutoff,
min_count);
}
catch (std::exception const& e)
{
cerr << e.what() << endl;
return false;
}
return true;
}
bool
MicroCore::get_output_histogram(
COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request const& req,
COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res) const
{
// based on bool core_rpc_server::on_get_output_histogram(const ...
MicroCore::histogram_map histogram;
if (!get_output_histogram(req.amounts,
req.min_count,
histogram,
req.unlocked,
req.recent_cutoff))
{
return false;
}
res.histogram.clear();
res.histogram.reserve(histogram.size());
for (auto const& i: histogram)
{
if (std::get<0>(i.second)
>= req.min_count
&& (std::get<0>(i.second) <= req.max_count
|| req.max_count == 0))
res.histogram.push_back(
COMMAND_RPC_GET_OUTPUT_HISTOGRAM::entry(
i.first,
std::get<0>(i.second),
std::get<1>(i.second),
std::get<2>(i.second)));
}
res.status = CORE_RPC_STATUS_OK;
return true;
}
hw::device* const
MicroCore::get_device() const
{
return m_device;
}
bool
MicroCore::init_success() const
{
return initialization_succeded;
}
MicroCore::~MicroCore()
{
//cout << "\n\nMicroCore::~MicroCore()\n\n";
if (initialization_succeded)
{
//core_storage.get_db().safesyncmode(true);
if (core_storage.get_db().is_open())
core_storage.get_db().close();
//cout << "\n\n core_storage.get_db().close();;\n\n";
}
}
}

@ -1,212 +0,0 @@
//
// Created by mwo on 5/11/15.
//
#ifndef XMREG01_MICROCORE_H
#define XMREG01_MICROCORE_H
#include <iostream>
#include <random>
#include "monero_headers.h"
#include "tools.h"
namespace xmreg
{
using namespace cryptonote;
using namespace crypto;
using namespace std;
/**
* Micro version of cryptonode::core class
* Micro version of constructor,
* init and destructor are implemted.
*
* Just enough to read the blockchain
* database for use in the example.
*/
class MicroCore {
string blockchain_path;
tx_memory_pool m_mempool;
Blockchain core_storage;
hw::device* m_device;
network_type nettype;
bool initialization_succeded {false};
public:
// <amoumt,
// tuple<total_instances, unlocked_instances, recent_instances>
using histogram_map = std::map<uint64_t,
std::tuple<uint64_t, uint64_t, uint64_t>>;
MicroCore();
/**
* Initialized the MicroCore object.
*
* Create BlockchainLMDB on the heap.
* Open database files located in blockchain_path.
* Initialize m_blockchain_storage with the BlockchainLMDB object.
*/
virtual bool
init(const string& _blockchain_path, network_type nt);
virtual Blockchain const&
get_core() const;
virtual tx_memory_pool const&
get_mempool() const;
virtual hw::device* const
get_device() const;
virtual void
get_output_key(const uint64_t& amount,
const vector<uint64_t>& absolute_offsets,
vector<cryptonote::output_data_t>& outputs)
{
core_storage.get_db()
.get_output_key(amount, absolute_offsets, outputs);
}
virtual output_data_t
get_output_key(uint64_t amount,
uint64_t global_amount_index)
{
return core_storage.get_db()
.get_output_key(amount, global_amount_index);
}
virtual bool
get_transactions(
const std::vector<crypto::hash>& txs_ids,
std::vector<transaction>& txs,
std::vector<crypto::hash>& missed_txs) const
{
return core_storage.get_transactions(txs_ids, txs, missed_txs);
}
virtual std::vector<block>
get_blocks_range(const uint64_t& h1, const uint64_t& h2) const
{
return core_storage.get_db().get_blocks_range(h1, h2);
}
virtual uint64_t
get_tx_unlock_time(crypto::hash const& tx_hash) const
{
return core_storage.get_db().get_tx_unlock_time(tx_hash);
}
virtual bool
have_tx(crypto::hash const& tx_hash) const
{
return core_storage.have_tx(tx_hash);
}
virtual bool
tx_exists(crypto::hash const& tx_hash, uint64_t& tx_id) const
{
return core_storage.get_db().tx_exists(tx_hash, tx_id);
}
virtual tx_out_index
get_output_tx_and_index(uint64_t const& amount, uint64_t const& index) const
{
return core_storage.get_db().get_output_tx_and_index(amount, index);
}
virtual uint64_t
get_tx_block_height(crypto::hash const& tx_hash) const
{
return core_storage.get_db().get_tx_block_height(tx_hash);
}
virtual std::vector<uint64_t>
get_tx_amount_output_indices(uint64_t const& tx_id) const
{
return core_storage.get_db().get_tx_amount_output_indices(tx_id);
}
virtual bool
get_mempool_txs(
std::vector<tx_info>& tx_infos,
std::vector<spent_key_image_info>& key_image_infos) const
{
return m_mempool.get_transactions_and_spent_keys_info(
tx_infos, key_image_infos);
}
virtual uint64_t
get_current_blockchain_height() const
{
return core_storage.get_current_blockchain_height();
}
virtual bool
get_output_histogram(
vector<uint64_t> const& amounts,
uint64_t min_count,
histogram_map& histogram,
bool unlocked = true,
uint64_t recent_cutoff = 0) const;
// mimicks core_rpc_server::on_get_output_histogram(..)
virtual bool
get_output_histogram(
COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request const& req,
COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res) const;
virtual bool
get_outs(COMMAND_RPC_GET_OUTPUTS_BIN::request const& req,
COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const
{
return core_storage.get_outs(req, res);
}
virtual uint64_t
get_dynamic_base_fee_estimate(uint64_t const& grace_blocks) const
{
return core_storage.get_dynamic_base_fee_estimate(grace_blocks);
}
bool
get_block_complete_entry(block const& b, block_complete_entry& bce);
virtual bool
get_block_from_height(uint64_t height, block& blk) const;
virtual bool
get_tx(crypto::hash const& tx_hash, transaction& tx) const;
virtual bool
decrypt_payment_id(crypto::hash8 &payment_id,
public_key const& public_key,
secret_key const& secret_key)
{
return m_device->decrypt_payment_id(payment_id,
public_key,
secret_key);
}
virtual bool
init_success() const;
virtual ~MicroCore();
};
}
#endif //XMREG01_MICROCORE_H

@ -1,49 +0,0 @@
//
// Created by mwo on 12/07/18.
//
#include "easylogging++.h"
#include "om_log.h"
#include "MysqlPing.h"
namespace xmreg
{
MysqlPing::MysqlPing(
std::shared_ptr<MySqlConnector> _conn,
uint64_t _ping_time)
: conn {_conn}, ping_time {_ping_time}
{}
void
MysqlPing::operator()()
{
while (keep_looping)
{
std::this_thread::sleep_for(chrono::seconds(ping_time));
if (auto c = conn.lock())
{
if (!c->ping())
{
OMERROR << "Pinging mysql failed. Stoping mysql pinging thread.";
why_stoped = StopReason::PingFailed;
break;
}
OMINFO << "Mysql ping successful." ;
}
else
{
OMERROR << "std::weak_ptr<MySqlConnector> conn expired!";
why_stoped = StopReason::PointerExpired;
break;
}
++counter;
}
}
}

File diff suppressed because it is too large Load Diff

@ -8,18 +8,14 @@
#include <iostream>
#include <functional>
#include "version.h"
#include "CurrentBlockchainStatus.h"
#include "MySqlAccounts.h"
#include "../gen/version.h"
#include "db/MySqlAccounts.h"
#include "../ext/restbed/source/restbed"
#ifndef MAKE_RESOURCE
#define MAKE_RESOURCE(name) auto name = open_monero.make_resource( \
&xmreg::YourMoneroRequests::name, "/" + string(#name));
&xmreg::OpenMoneroRequests::name, "/" + string(#name));
#endif
@ -32,7 +28,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define OPENMONERO_RPC_VERSION_MAJOR 1
#define OPENMONERO_RPC_VERSION_MINOR 3
#define OPENMONERO_RPC_VERSION_MINOR 5
#define MAKE_OPENMONERO_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define OPENMONERO_RPC_VERSION \
MAKE_OPENMONERO_RPC_VERSION(OPENMONERO_RPC_VERSION_MAJOR, OPENMONERO_RPC_VERSION_MINOR)
@ -58,7 +54,7 @@ struct handel_
};
class YourMoneroRequests
class OpenMoneroRequests
{
// this manages all mysql queries
@ -67,7 +63,7 @@ class YourMoneroRequests
public:
YourMoneroRequests(shared_ptr<MySqlAccounts> _acc,
OpenMoneroRequests(shared_ptr<MySqlAccounts> _acc,
shared_ptr<CurrentBlockchainStatus> _current_bc_status);
/**
@ -114,7 +110,7 @@ public:
get_version(const shared_ptr< Session > session, const Bytes & body);
shared_ptr<Resource>
make_resource(function< void (YourMoneroRequests&, const shared_ptr< Session >, const Bytes& ) > handle_func,
make_resource(function< void (OpenMoneroRequests&, const shared_ptr< Session >, const Bytes& ) > handle_func,
const string& path);
static void
@ -135,7 +131,7 @@ public:
body_to_json(const Bytes & body);
inline uint64_t
get_current_blockchain_height();
get_current_blockchain_height() const;
private:
@ -147,15 +143,33 @@ private:
json& j_response);
inline void
session_close(const shared_ptr< Session > session, string response_body);
bool
parse_request(const Bytes& body,
vector<string>& values_map,
json& j_request,
json& j_response);
json& j_response) const;
boost::optional<XmrAccount>
create_account(string const& xmr_address,
string const& view_key) const;
bool
make_search_thread(XmrAccount& acc) const;
boost::optional<XmrAccount>
select_account(string const& xmr_address,
string const& view_key,
bool create_if_notfound = true) const;
boost::optional<XmrPayment>
select_payment(XmrAccount const& xmr_account) const;
void
session_close(
const shared_ptr< Session > session,
json& j_response,
int return_code = OK,
string error_msg = "") const;
};

@ -1,280 +0,0 @@
//
// Created by mwo on 13/02/17.
//
#include "OutputInputIdentification.h"
namespace xmreg
{
OutputInputIdentification::OutputInputIdentification(
const address_parse_info* _a,
const secret_key* _v,
const transaction* _tx,
crypto::hash const& _tx_hash,
bool is_coinbase,
std::shared_ptr<CurrentBlockchainStatus> _current_bc_status)
: current_bc_status {_current_bc_status}
{
address_info = _a;
viewkey = _v;
tx = _tx;
tx_pub_key = xmreg::get_tx_pub_key_from_received_outs(*tx);
tx_is_coinbase = is_coinbase;
tx_hash = _tx_hash;
is_rct = (tx->version == 2);
if (is_rct)
{
rct_type = tx->rct_signatures.type;
}
if (!generate_key_derivation(tx_pub_key, *viewkey, derivation))
{
OMERROR << "Cant get derived key for: " << "\n"
<< "pub_tx_key: " << get_tx_pub_key_str() << " and "
<< "prv_view_key" << viewkey;;
//throw OutputInputIdentificationException(
// "Cant get derived key for a tx");
status = INTERNAL_STATUS::CANT_DERIVE_KEY;
return;
}
status = INTERNAL_STATUS::OK;
}
uint64_t
OutputInputIdentification::get_mixin_no()
{
if (mixin_no == 0 && !tx_is_coinbase)
mixin_no = xmreg::get_mixin_no(*tx);
return mixin_no;
}
void
OutputInputIdentification::identify_outputs()
{
// <public_key , amount , out idx>
vector<tuple<txout_to_key, uint64_t, uint64_t>> outputs
= get_ouputs_tuple(*tx);
for (auto& out: outputs)
{
txout_to_key const& txout_k = std::get<0>(out);
uint64_t amount = std::get<1>(out);
uint64_t output_idx_in_tx = std::get<2>(out);
// get the tx output public key
// that normally would be generated for us,
// if someone had sent us some xmr.
public_key generated_tx_pubkey;
derive_public_key(derivation,
output_idx_in_tx,
address_info->address.m_spend_public_key,
generated_tx_pubkey);
// check if generated public key matches the current output's key
bool mine_output = (txout_k.key == generated_tx_pubkey);
// placeholder variable for ringct outputs info
// that we need to save in database
string rtc_outpk;
string rtc_mask;
string rtc_amount;
// if mine output has RingCT, i.e., tx version is 2
// need to decode its amount. otherwise its zero.
if (mine_output && tx->version == 2)
{
// initialize with regular amount value
// for ringct, except coinbase, it will be 0
uint64_t rct_amount_val = amount;
// cointbase txs have amounts in plain sight.
// so use amount from ringct, only for non-coinbase txs
if (!tx_is_coinbase)
{
bool r;
// for ringct non-coinbase txs, these values are given
// with txs.
// coinbase ringctx dont have this information. we will provide
// them only when needed, in get_unspent_outputs. So go there
// to see how we deal with ringct coinbase txs when we spent
// them
// go to CurrentBlockchainStatus::construct_output_rct_field
// to see how we deal with coinbase ringct that are used
// as mixins
rtc_outpk = pod_to_hex(tx->rct_signatures
.outPk[output_idx_in_tx].mask);
rtc_mask = pod_to_hex(tx->rct_signatures
.ecdhInfo[output_idx_in_tx].mask);
rtc_amount = pod_to_hex(tx->rct_signatures
.ecdhInfo[output_idx_in_tx].amount);
rct::key mask = tx->rct_signatures
.ecdhInfo[output_idx_in_tx].mask;
r = decode_ringct(tx->rct_signatures,
tx_pub_key,
*viewkey,
output_idx_in_tx,
mask,
rct_amount_val);
if (!r)
{
OMERROR << "Cant decode ringCT!";
throw OutputInputIdentificationException(
"Cant decode ringCT!");
}
amount = rct_amount_val;
} // if (!tx_is_coinbase)
} // if (mine_output && tx.version == 2)
if (mine_output)
{
total_received += amount;
identified_outputs.emplace_back(
output_info{
txout_k.key, amount, output_idx_in_tx,
rtc_outpk, rtc_mask, rtc_amount
});
} // if (mine_output)
} // for (const auto& out: outputs)
}
void
OutputInputIdentification::identify_inputs(
unordered_map<public_key, uint64_t> const& known_outputs_keys)
{
vector<txin_to_key> input_key_imgs = xmreg::get_key_images(*tx);
size_t search_misses {0};
// make timescale maps for mixins in input
for (txin_to_key const& in_key: input_key_imgs)
{
// get absolute offsets of mixins
std::vector<uint64_t> absolute_offsets
= cryptonote::relative_output_offsets_to_absolute(
in_key.key_offsets);
// get public keys of outputs used in the mixins that
// match to the offests
std::vector<cryptonote::output_data_t> mixin_outputs;
if (!current_bc_status->get_output_keys(in_key.amount,
absolute_offsets,
mixin_outputs))
{
OMERROR << "Mixins key images not found";
continue;
}
// indicates whether we found any matching mixin in the current input
bool found_a_match {false};
// for each found output public key check if its ours or not
for (size_t count = 0; count < absolute_offsets.size(); ++count)
{
// get basic information about mixn's output
cryptonote::output_data_t const& output_data
= mixin_outputs[count];
//cout << " - output_public_key_str: "
// << output_public_key_str;
// before going to the mysql, check our known outputs cash
// if the key exists. Its much faster than going to mysql
// for this.
auto it = known_outputs_keys.find(output_data.pubkey);
if (it != known_outputs_keys.end())
{
// this seems to be our mixin.
// save it into identified_inputs vector
identified_inputs.push_back(input_info {
pod_to_hex(in_key.k_image),
it->second, // amount
output_data.pubkey});
//cout << "\n\n" << it->second << endl;
found_a_match = true;
}
} // for (const cryptonote::output_data_t& output_data: outputs)
if (found_a_match == false)
{
// if we didnt find any match, break of the look.
// there is no reason to check remaining key images
// as when we spent something, our outputs should be
// in all inputs in a given txs. Thus, if a single input
// is without our output, we can assume this tx does
// not contain any of our spendings.
// just to be sure before we break out of this loop,
// do it only after two misses
if (++search_misses > 2)
break;
}
} // for (txin_to_key const& in_key: input_key_imgs)
}
string const&
OutputInputIdentification::get_tx_hash_str()
{
if (tx_hash_str.empty())
tx_hash_str = pod_to_hex(tx_hash);
return tx_hash_str;
}
string const&
OutputInputIdentification::get_tx_prefix_hash_str()
{
if (tx_prefix_hash_str.empty())
{
tx_prefix_hash = get_transaction_prefix_hash(*tx);
tx_prefix_hash_str = pod_to_hex(tx_prefix_hash);
}
return tx_prefix_hash_str;
}
string const&
OutputInputIdentification::get_tx_pub_key_str()
{
if (tx_pub_key_str.empty())
tx_pub_key_str = pod_to_hex(tx_pub_key);
return tx_pub_key_str;
}
}

@ -1,178 +0,0 @@
//
// Created by mwo on 13/02/17.
//
#pragma once
#include "CurrentBlockchainStatus.h"
#include "tools.h"
#include <map>
#include <utility>
namespace xmreg
{
class OutputInputIdentificationException: public std::runtime_error
{
using std::runtime_error::runtime_error;
};
/*
* This class takes user address, viewkey
* and searches for outputs in a given tx
* and possible spendings/inputs.
*
* It is the key class for identification of incoming monero
* and candidate outcoming xmr for a user in a blockchain and in
* mempool.
*
* searching for our incoming and outgoing xmr has two components.
*
* FIRST. to search for the incoming xmr, we use address, viewkey and
* outputs public key. Its straight forward, as this is what viewkey was
* designed to do.
*
* SECOND. Searching for spendings (i.e., key images) is more difficult,
* because we dont have spendkey. But what we can do is, we can look for
* candidate key images. And this can be achieved by checking if any mixin
* in associated with the given key image, is our output. If it is our output,
* then we assume its our key image (i.e. we spend this output). Off course
* this is only
* assumption as our outputs can be used in key images of others for their
* mixin purposes. Thus, we sent to the frontend the list of key images
* that we think are yours, and the frontend, because it has spendkey,
* can filter out false positives.
*/
class OutputInputIdentification
{
public:
// quick workaround to issue: 128
// so that search threads dont terminate
// when an error of creating derived key occurs
enum class INTERNAL_STATUS {FAIL, OK, CANT_DERIVE_KEY};
// define a structure to keep information about found
// outputs that we can need in later parts.
struct output_info
{
public_key pub_key;
uint64_t amount;
uint64_t idx_in_tx;
string rtc_outpk;
string rtc_mask;
string rtc_amount;
};
// define a structure to keep information about found
// inputs that we can need in later parts.
struct input_info
{
string key_img;
uint64_t amount;
public_key out_pub_key;
};
crypto::hash tx_hash;
crypto::hash tx_prefix_hash;
public_key tx_pub_key;
string tx_hash_str;
string tx_prefix_hash_str;
string tx_pub_key_str;
bool tx_is_coinbase;
bool is_rct;
uint8_t rct_type;
uint64_t total_received {0};
uint64_t mixin_no {0};
// for each output, in a tx, check if it belongs
// to the given account of specific address and viewkey
// public transaction key is combined with our viewkey
// to create, so called, derived key.
key_derivation derivation;
vector<output_info> identified_outputs;
vector<input_info> identified_inputs;
std::shared_ptr<CurrentBlockchainStatus> current_bc_status;
INTERNAL_STATUS status = INTERNAL_STATUS::FAIL;
// default constructor. Useful for unit tests
OutputInputIdentification() = default;
OutputInputIdentification(const address_parse_info* _a,
const secret_key* _v,
const transaction* _tx,
crypto::hash const& _tx_hash,
bool is_coinbase,
std::shared_ptr<CurrentBlockchainStatus>
_current_bc_status);
/**
* FIRST step. search for the incoming xmr using address, viewkey and
* outputs public keys.
*/
virtual void
identify_outputs();
/**
* SECOND step. search for possible our inputs.
* We do this by comparing mixin public keys with
* our known output keys. If a metch is found, we assume
* that associated input is ours
*
* The known_outputs_keys parameter is optional. But when we have
* it, the identification is faster as we just check mixins public keys
* if they are in known_outputs_keys.
*
* searching without known_outputs_keys is not implemented yet here
* but it was done for the onion explorer. so later basically just
* copy and past here.
*
* known_outputs_keys is pair of <output public key, output amount>
*
*/
virtual void
identify_inputs(unordered_map<public_key, uint64_t> const&
known_outputs_keys);
virtual string const&
get_tx_hash_str();
virtual string const&
get_tx_prefix_hash_str();
virtual string const&
get_tx_pub_key_str();
virtual uint64_t
get_mixin_no();
virtual ~OutputInputIdentification() = default;
private:
// address and viewkey for this search thread.
const address_parse_info* address_info;
const secret_key* viewkey;
// transaction that is beeing search
const transaction* tx;
};
}

@ -2,95 +2,26 @@
// Created by mwo on 13/04/16.
//
#include "easylogging++.h"
#include "om_log.h"
#include "RPCCalls.h"
namespace xmreg
{
RPCCalls::RPCCalls(string _deamon_url, uint64_t _timeout)
: deamon_url {_deamon_url}, timeout_time {_timeout}
RPCCalls::RPCCalls(string _deamon_url, chrono::seconds _timeout)
: deamon_url {_deamon_url}, rpc_timeout {_timeout}
{
epee::net_utils::parse_url(deamon_url, url);
port = std::to_string(url.port);
timeout_time_ms = std::chrono::milliseconds {timeout_time};
m_http_client.set_server(
deamon_url,
boost::optional<epee::net_utils::http::login>{});
}
RPCCalls::RPCCalls(RPCCalls&& a)
{
std::lock_guard<std::mutex> guard(a.m_daemon_rpc_mutex);
deamon_url = std::move(a.deamon_url);
timeout_time = a.timeout_time;
timeout_time_ms = a.timeout_time_ms;
url = std::move(a.url);
port = std::move(port);
// we can't move or copy m_http_client,
// so we just initialize it from zero
m_http_client.set_server(
deamon_url,
boost::optional<epee::net_utils::http::login>{});
// after the move, disconned the a object
a.m_http_client.disconnect();
cout << "\n RPCCalls(RPCCalls&& a) " << endl;
}
RPCCalls&
RPCCalls::operator=(RPCCalls&& a)
{
if (*this == a)
return *this;
std::unique_lock<std::mutex> lhs_lk(m_daemon_rpc_mutex, std::defer_lock);
std::unique_lock<std::mutex> rhs_lk(a.m_daemon_rpc_mutex, std::defer_lock);
std::lock(lhs_lk, rhs_lk);
deamon_url = std::move(a.deamon_url);
timeout_time = a.timeout_time;
timeout_time_ms = a.timeout_time_ms;
url = std::move(a.url);
port = std::move(port);
// we can't move or copy m_http_client,
// so we just initialize it from zero
m_http_client.set_server(
deamon_url,
boost::optional<epee::net_utils::http::login>{});
// after the move, disconned the a object
a.m_http_client.disconnect();
cout << "\n RPCCalls& operator=(RPCCalls&& a) " << endl;
return *this;
}
bool
RPCCalls::operator==(RPCCalls const& a)
{
return deamon_url == a.deamon_url;
}
bool
RPCCalls::operator!=(RPCCalls const& a)
{
return !(*this == a);
}
bool
RPCCalls::connect_to_monero_deamon()
{
@ -99,11 +30,9 @@ RPCCalls::connect_to_monero_deamon()
return true;
}
return m_http_client.connect(timeout_time_ms);
return m_http_client.connect(rpc_timeout);
}
bool
RPCCalls::commit_tx(
const string& tx_blob,
@ -114,46 +43,35 @@ RPCCalls::commit_tx(
COMMAND_RPC_SEND_RAW_TX::response res;
req.tx_as_hex = tx_blob;
req.do_not_relay = do_not_relay;
req.do_not_relay = do_not_relay;
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
if (!connect_to_monero_deamon())
{
cerr << "get_current_height: not connected to deamon" << endl;
return false;
}
bool r {false};
bool r = epee::net_utils::invoke_http_json(
"/sendrawtransaction", req, res,
m_http_client, timeout_time_ms);
if (!r || res.status == "Failed")
{
error_msg = res.reason;
if (error_msg.empty())
{
error_msg = "Reason not given by daemon. A guess is 'Failed to check ringct signatures!'.";
}
cerr << "Error sending tx: " << res.reason << endl;
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
return false;
r = epee::net_utils::invoke_http_json(
"/sendrawtransaction", req, res,
m_http_client, rpc_timeout);
}
else if (res.status == "BUSY")
{
error_msg = "Deamon is BUSY. Cant sent now " + res.reason;
cerr << "Error sending tx: " << error_msg << endl;
if (!r || !check_if_response_is_ok(res, error_msg))
{
if (res.reason.empty())
error_msg += "Reason not given by daemon.";
else
error_msg += res.reason;
OMERROR << "Error sending tx. " << error_msg;
return false;
}
if (do_not_relay)
{
cout << "Tx accepted by deamon but not relayed (useful for testing of constructing txs)" << endl;
OMINFO << "Tx accepted by deamon but "
"not relayed (useful for testing "
"of constructing txs)";
}
return true;
@ -173,4 +91,68 @@ RPCCalls::commit_tx(
}
bool
RPCCalls::get_current_height(uint64_t& current_height)
{
COMMAND_RPC_GET_HEIGHT::request req;
COMMAND_RPC_GET_HEIGHT::response res;
bool r {false};
{
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
r = epee::net_utils::invoke_http_json(
"/getheight",
req, res, m_http_client, rpc_timeout);
}
string error_msg;
if (!r || !check_if_response_is_ok(res, error_msg))
{
OMERROR << "Error getting blockchain height. "
<< error_msg;
return false;
}
current_height = res.height;
return true;
}
template <typename Command>
bool
RPCCalls::check_if_response_is_ok(
Command const& res,
string& error_msg) const
{
if (res.status == CORE_RPC_STATUS_BUSY)
{
error_msg = "Deamon is BUSY. Cant sent now. ";
return false;
}
if (res.status != CORE_RPC_STATUS_OK)
{
error_msg = "RPC failed. ";
return false;
}
return true;
}
template
bool RPCCalls::check_if_response_is_ok<
COMMAND_RPC_SEND_RAW_TX::response>(
COMMAND_RPC_SEND_RAW_TX::response const& res,
string& error_msg) const;
template
bool RPCCalls::check_if_response_is_ok<
COMMAND_RPC_GET_HEIGHT::response>(
COMMAND_RPC_GET_HEIGHT::response const& res,
string& error_msg) const;
}

@ -6,9 +6,10 @@
#ifndef CROWXMR_RPCCALLS_H
#define CROWXMR_RPCCALLS_H
#include "monero_headers.h"
#include "src/monero_headers.h"
#include <mutex>
#include <chrono>
namespace xmreg
{
@ -17,13 +18,14 @@ using namespace cryptonote;
using namespace crypto;
using namespace std;
using namespace std::chrono_literals;
class RPCCalls
{
string deamon_url;
uint64_t timeout_time;
std::chrono::milliseconds timeout_time_ms;
chrono::seconds rpc_timeout;
epee::net_utils::http::url_content url;
@ -36,18 +38,7 @@ class RPCCalls
public:
RPCCalls(string _deamon_url = "http:://127.0.0.1:18081",
uint64_t _timeout = 200000);
RPCCalls(RPCCalls&& a);
RPCCalls&
operator=(RPCCalls&& a);
virtual bool
operator==(RPCCalls const& a);
virtual bool
operator!=(RPCCalls const& a);
chrono::seconds _timeout = 3min + 30s);
virtual bool
connect_to_monero_deamon();
@ -61,8 +52,17 @@ public:
commit_tx(tools::wallet2::pending_tx& ptx,
string& error_msg);
virtual bool
get_current_height(uint64_t& current_height);
virtual ~RPCCalls() = default;
protected:
template <typename Command>
bool
check_if_response_is_ok(Command const& res,
string& error_msg) const;
};

@ -1,13 +1,13 @@
#include "RandomOutputs.h"
#include "CurrentBlockchainStatus.h"
namespace xmreg
{
RandomOutputs::RandomOutputs(
MicroCore const& _mcore,
CurrentBlockchainStatus const* _cbs,
vector<uint64_t> const& _amounts,
uint64_t _outs_count)
: mcore {_mcore},
: cbs {_cbs},
amounts {_amounts},
outs_count {_outs_count}
{
@ -39,7 +39,7 @@ RandomOutputs::get_output_pub_key(
req.outputs.push_back(get_outputs_out {amount, global_output_index});
if (!mcore.get_outs(req, res))
if (!cbs->get_outs(req, res))
{
OMERROR << "mcore->get_outs(req, res) failed";
return false;
@ -65,7 +65,7 @@ RandomOutputs::find_random_outputs()
req.min_count = outs_count;
req.max_count = 0;
if (!mcore.get_output_histogram(req, res))
if (!cbs->get_output_histogram(req, res))
{
OMERROR << "mcore->get_output_histogram(req, res)";
return false;
@ -80,15 +80,16 @@ RandomOutputs::find_random_outputs()
// find histogram_entry for amount that we look
// random outputs for
auto const hist_entry_it = std::find_if(
begin(res.histogram), end(res.histogram),
[&amount](histogram_entry const& he)
{
return amount == he.amount;
});
std::begin(res.histogram), std::end(res.histogram),
[&amount](auto const& he)
{
return amount == he.amount;
});
if (hist_entry_it == res.histogram.end())
{
OMERROR << "Could not find amount: it == res.histogram.end()\n";
OMERROR << "Could not find amount: it "
"== res.histogram.end()\n";
return false;
}

@ -2,12 +2,14 @@
#define RANDOMOUTPUTS_H
#include "om_log.h"
#include "MicroCore.h"
#include "src/MicroCore.h"
namespace xmreg
{
class CurrentBlockchainStatus;
/**
* @brief Returns random ouputs for given amounts
*
@ -46,7 +48,7 @@ public:
using outs_for_amount_v = vector<outs_for_amount>;
RandomOutputs(MicroCore const& _mcore,
RandomOutputs(CurrentBlockchainStatus const* _cbs,
vector<uint64_t> const& _amounts,
uint64_t _outs_count);
@ -59,7 +61,8 @@ public:
virtual ~RandomOutputs() = default;
protected:
MicroCore const& mcore;
//MicroCore const& mcore;
CurrentBlockchainStatus const* cbs;
vector<uint64_t> amounts;
uint64_t outs_count;

@ -17,13 +17,15 @@ ThreadRAII::~ThreadRAII()
{
if (action == DtorAction::join)
{
//std::cout << "\nThreadRAII::~ThreadRAII() t.join()\n";
//std::cout << "\nThreadRAII::~ThreadRAII() t.join()\n"
// << std::endl;
t.join();
}
else
{
t.detach();
//std::cout << "\nThreadRAII::~ThreadRAII() t.detach()\n";
//std::cout << "\nThreadRAII::~ThreadRAII() t.detach()\n"
// << std::endl;
t.detach();
}
}
}

@ -15,7 +15,7 @@ namespace xmreg
class ThreadRAII
{
public:
enum class DtorAction { join, detach};
enum class DtorAction {join, detach};
ThreadRAII(std::thread&& _t, DtorAction _action);

File diff suppressed because it is too large Load Diff

@ -1,7 +1,6 @@
#pragma once
#include "MySqlAccounts.h"
#include "OutputInputIdentification.h"
#include "db/MySqlAccounts.h"
#include <memory>
#include <mutex>
@ -14,6 +13,9 @@ namespace xmreg
using namespace std;
using chrono::seconds;
using namespace literals::chrono_literals;
class XmrAccount;
class MySqlAccounts;
@ -40,9 +42,19 @@ private:
// how long should the search thread be live after no request
// are coming from the frontend. For example, when a user finishes
// using the service.
static uint64_t thread_search_life; // in seconds
static seconds thread_search_life;
// indicate that a thread loop should keep running
std::atomic_bool continue_search {true};
// this acctually indicates whether thread loop finished
// its execution
std::atomic_bool searching_is_ongoing {false};
bool continue_search {true};
// marked true when we set new searched block value
// from other thread. for example, when we import account
// we set it to 0
std::atomic_bool searched_block_got_updated {false};
// to store last exception thrown in the search thread
// using this, a main thread can get info what went wrong here
@ -51,7 +63,7 @@ private:
mutex getting_eptr;
mutex getting_known_outputs_keys;
uint64_t last_ping_timestamp;
seconds last_ping_timestamp;
atomic<uint64_t> searched_blk_no;
@ -78,12 +90,14 @@ private:
address_parse_info address;
secret_key viewkey;
string address_prefix;
public:
// make default constructor. useful in testing
TxSearch() = default;
TxSearch(XmrAccount& _acc,
TxSearch(XmrAccount const& _acc,
std::shared_ptr<CurrentBlockchainStatus> _current_bc_status);
virtual void
@ -98,7 +112,7 @@ public:
virtual uint64_t
get_searched_blk_no() const;
virtual uint64_t
virtual seconds
get_current_timestamp() const;
virtual void
@ -160,7 +174,7 @@ public:
get_xmr_address_viewkey() const;
static void
set_search_thread_life(uint64_t life_seconds);
set_search_thread_life(seconds life_seconds);
virtual bool
delete_existing_tx_if_exists(string const& tx_hash);

@ -1,7 +1,7 @@
#ifndef TXUNLOCKCHECKER_H
#define TXUNLOCKCHECKER_H
#include "monero_headers.h"
#include "src/monero_headers.h"
namespace xmreg
{

@ -5,8 +5,8 @@
#define MYSQLPP_SSQLS_NO_STATICS 1
#include "MySqlAccounts.h"
#include "TxSearch.h"
#include "CurrentBlockchainStatus.h"
//#include "TxSearch.h"
#include "../CurrentBlockchainStatus.h"
#include "ssqlses.h"
@ -19,7 +19,8 @@ MysqlInputs::MysqlInputs(shared_ptr<MySqlConnector> _conn)
{}
bool
MysqlInputs::select_for_out(const uint64_t& output_id, vector<XmrInput>& ins)
MysqlInputs::select_for_out(const uint64_t& output_id,
vector<XmrInput>& ins)
{
try
{
@ -77,7 +78,8 @@ MysqlOutpus::exist(const string& output_public_key_str, XmrOutput& out)
}
MysqlTransactions::MysqlTransactions(shared_ptr<MySqlConnector> _conn): conn {_conn}
MysqlTransactions::MysqlTransactions(shared_ptr<MySqlConnector> _conn)
: conn {_conn}
{}
uint64_t
@ -87,9 +89,10 @@ MysqlTransactions::mark_spendable(const uint64_t& tx_id_no, bool spendable)
{
conn->check_if_connected();
Query query = conn->query(spendable ?
XmrTransaction::MARK_AS_SPENDABLE_STMT
: XmrTransaction::MARK_AS_NONSPENDABLE_STMT);
Query query = conn->query(
spendable ?
XmrTransaction::MARK_AS_SPENDABLE_STMT
: XmrTransaction::MARK_AS_NONSPENDABLE_STMT);
query.parse();
@ -131,7 +134,8 @@ MysqlTransactions::delete_tx(const uint64_t& tx_id_no)
bool
MysqlTransactions::exist(const uint64_t& account_id, const string& tx_hash_str, XmrTransaction& tx)
MysqlTransactions::exist(const uint64_t& account_id,
const string& tx_hash_str, XmrTransaction& tx)
{
try
{
@ -161,7 +165,8 @@ MysqlTransactions::exist(const uint64_t& account_id, const string& tx_hash_str,
bool
MysqlTransactions::get_total_recieved(const uint64_t& account_id, uint64_t& amount)
MysqlTransactions::get_total_recieved(const uint64_t& account_id,
uint64_t& amount)
{
try
{
@ -190,7 +195,8 @@ MysqlPayments::MysqlPayments(shared_ptr<MySqlConnector> _conn): conn {_conn}
{}
bool
MysqlPayments::select_by_payment_id(const string& payment_id, vector<XmrPayment>& payments)
MysqlPayments::select_by_payment_id(const string& payment_id,
vector<XmrPayment>& payments)
{
try
@ -214,17 +220,20 @@ MysqlPayments::select_by_payment_id(const string& payment_id, vector<XmrPayment>
return false;
}
MySqlAccounts::MySqlAccounts(shared_ptr<CurrentBlockchainStatus> _current_bc_status)
MySqlAccounts::MySqlAccounts(
shared_ptr<CurrentBlockchainStatus> _current_bc_status)
: current_bc_status {_current_bc_status}
{
// create connection to the mysql
conn = make_shared<MySqlConnector>();
conn = make_shared<MySqlConnector>(
new mysqlpp::ReconnectOption(true));
_init();
}
MySqlAccounts::MySqlAccounts(shared_ptr<CurrentBlockchainStatus> _current_bc_status,
shared_ptr<MySqlConnector> _conn)
MySqlAccounts::MySqlAccounts(
shared_ptr<CurrentBlockchainStatus> _current_bc_status,
shared_ptr<MySqlConnector> _conn)
: current_bc_status {_current_bc_status}
{
conn = _conn;
@ -246,6 +255,8 @@ MySqlAccounts::select(const string& address, XmrAccount& account)
vector<XmrAccount> res;
query.storein(res, address);
//while(query.more_results());
if (!res.empty())
{
account = std::move(res.at(0));
@ -292,7 +303,8 @@ MySqlAccounts::insert(const T& data_to_insert)
template
uint64_t MySqlAccounts::insert<XmrAccount>(const XmrAccount& data_to_insert);
template
uint64_t MySqlAccounts::insert<XmrTransaction>(const XmrTransaction& data_to_insert);
uint64_t MySqlAccounts::insert<XmrTransaction>(
const XmrTransaction& data_to_insert);
template
uint64_t MySqlAccounts::insert<XmrOutput>(const XmrOutput& data_to_insert);
template
@ -327,9 +339,12 @@ MySqlAccounts::insert(const vector<T>& data_to_insert)
// Explicitly instantiate insert template for our tables
template
uint64_t MySqlAccounts::insert<XmrOutput>(const vector<XmrOutput>& data_to_insert);
uint64_t MySqlAccounts::insert<XmrOutput>(
const vector<XmrOutput>& data_to_insert);
template
uint64_t MySqlAccounts::insert<XmrInput>(const vector<XmrInput>& data_to_insert);
uint64_t MySqlAccounts::insert<XmrInput>(
const vector<XmrInput>& data_to_insert);
template <typename T, size_t query_no>
bool
@ -339,14 +354,18 @@ MySqlAccounts::select(uint64_t account_id, vector<T>& selected_data)
{
conn->check_if_connected();
Query query = conn->query((query_no == 1 ? T::SELECT_STMT : T::SELECT_STMT2));
Query query = conn->query((query_no == 1
? T::SELECT_STMT : T::SELECT_STMT2));
query.parse();
selected_data.clear();
query.storein(selected_data, account_id);
// this is confusing. So I get false from this method
// when this is empty and when there is some exception!
return !selected_data.empty();
//return true;
}
catch (std::exception const& e)
{
@ -357,25 +376,34 @@ MySqlAccounts::select(uint64_t account_id, vector<T>& selected_data)
}
template
bool MySqlAccounts::select<XmrAccount>(uint64_t account_id, vector<XmrAccount>& selected_data);
bool MySqlAccounts::select<XmrAccount>(uint64_t account_id,
vector<XmrAccount>& selected_data);
template
bool MySqlAccounts::select<XmrTransaction>(uint64_t account_id, vector<XmrTransaction>& selected_data);
bool MySqlAccounts::select<XmrTransaction>(uint64_t account_id,
vector<XmrTransaction>& selected_data);
template
bool MySqlAccounts::select<XmrOutput>(uint64_t account_id, vector<XmrOutput>& selected_data);
bool MySqlAccounts::select<XmrOutput>(uint64_t account_id,
vector<XmrOutput>& selected_data);
template // this will use SELECT_STMT2 which selectes based on transaction id, not account_id,
bool MySqlAccounts::select<XmrOutput, 2>(uint64_t tx_id, vector<XmrOutput>& selected_data);
template // this will use SELECT_STMT2 which selectes
// based on transaction id, not account_id,
bool MySqlAccounts::select<XmrOutput, 2>(uint64_t tx_id,
vector<XmrOutput>& selected_data);
template
bool MySqlAccounts::select<XmrInput>(uint64_t account_id, vector<XmrInput>& selected_data);
bool MySqlAccounts::select<XmrInput>(uint64_t account_id,
vector<XmrInput>& selected_data);
template
bool MySqlAccounts::select<XmrPayment>(uint64_t account_id, vector<XmrPayment>& selected_data);
bool MySqlAccounts::select<XmrPayment>(uint64_t account_id,
vector<XmrPayment>& selected_data);
template // this will use SELECT_STMT2 which selectes based on transaction id, not account_id,
bool MySqlAccounts::select<XmrInput, 2>(uint64_t tx_id, vector<XmrInput>& selected_data);
template // this will use SELECT_STMT2 which selectes
// based on transaction id, not account_id,
bool MySqlAccounts::select<XmrInput, 2>(uint64_t tx_id,
vector<XmrInput>& selected_data);
template <typename T>
@ -403,10 +431,12 @@ MySqlAccounts::update(T const& orginal_row, T const& new_row)
}
template
bool MySqlAccounts::update<XmrAccount>(XmrAccount const& orginal_row, XmrAccount const& new_row);
bool MySqlAccounts::update<XmrAccount>(
XmrAccount const& orginal_row, XmrAccount const& new_row);
template
bool MySqlAccounts::update<XmrPayment>(XmrPayment const& orginal_row, XmrPayment const& new_row);
bool MySqlAccounts::update<XmrPayment>(
XmrPayment const& orginal_row, XmrPayment const& new_row);
template <typename T>
bool
@ -415,12 +445,16 @@ MySqlAccounts::select_for_tx(uint64_t tx_id, vector<T>& selected_data)
return select<T, 2>(tx_id, selected_data);
}
template // this will use SELECT_STMT2 which selectes based on transaction id, not account_id,
bool MySqlAccounts::select_for_tx<XmrOutput>(uint64_t tx_id, vector<XmrOutput>& selected_data);
template // this will use SELECT_STMT2 which selectes based on
// transaction id, not account_id,
bool MySqlAccounts::select_for_tx<XmrOutput>(uint64_t tx_id,
vector<XmrOutput>& selected_data);
template // this will use SELECT_STMT2 which selectes based on transaction id, not account_id,
bool MySqlAccounts::select_for_tx<XmrInput>(uint64_t tx_id, vector<XmrInput>& selected_data);
template // this will use SELECT_STMT2 which selectes
//based on transaction id, not account_id,
bool MySqlAccounts::select_for_tx<XmrInput>(uint64_t tx_id,
vector<XmrInput>& selected_data);
template <typename T>
bool
@ -453,16 +487,20 @@ MySqlAccounts::select_by_primary_id(uint64_t id, T& selected_data)
}
//template
//bool MySqlAccounts::select_by_primary_id<XmrTransaction>(uint64_t id, XmrTransaction& selected_data);
//bool MySqlAccounts::select_by_primary_id<XmrTransaction>(
// uint64_t id, XmrTransaction& selected_data);
template
bool MySqlAccounts::select_by_primary_id<XmrInput>(uint64_t id, XmrInput& selected_data);
bool MySqlAccounts::select_by_primary_id<XmrInput>(
uint64_t id, XmrInput& selected_data);
template
bool MySqlAccounts::select_by_primary_id<XmrOutput>(uint64_t id, XmrOutput& selected_data);
bool MySqlAccounts::select_by_primary_id<XmrOutput>(
uint64_t id, XmrOutput& selected_data);
template
bool MySqlAccounts::select_by_primary_id<XmrPayment>(uint64_t id, XmrPayment& selected_data);
bool MySqlAccounts::select_by_primary_id<XmrPayment>(
uint64_t id, XmrPayment& selected_data);
bool
MySqlAccounts::select_txs_for_account_spendability_check(
@ -492,7 +530,8 @@ MySqlAccounts::select_txs_for_account_spendability_check(
if (no_row_updated != 1)
{
cerr << "no_row_updated != 1 due to xmr_accounts->mark_tx_spendable(tx.id)\n";
cerr << "no_row_updated != 1 due to "
"xmr_accounts->mark_tx_spendable(tx.id)\n";
return false;
}
@ -512,14 +551,16 @@ MySqlAccounts::select_txs_for_account_spendability_check(
if (blockchain_tx_id != tx.blockchain_tx_id)
{
// tx does not exist in blockchain, or its blockchain_id changed
// tx does not exist in blockchain, or its blockchain_id
// changed
// for example, it was orhpaned, and then readded.
uint64_t no_row_updated = delete_tx(tx.id.data);
if (no_row_updated != 1)
{
cerr << "no_row_updated != 1 due to xmr_accounts->delete_tx(tx.id)\n";
cerr << "no_row_updated != 1 due to "
"xmr_accounts->delete_tx(tx.id)\n";
return false;
}
@ -539,7 +580,8 @@ MySqlAccounts::select_txs_for_account_spendability_check(
// be spent anyway.
if (tx.unlock_time == 0)
tx.unlock_time = tx.height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
tx.unlock_time = tx.height
+ CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
} // else
@ -555,19 +597,22 @@ MySqlAccounts::select_txs_for_account_spendability_check(
bool
MySqlAccounts::select_inputs_for_out(const uint64_t& output_id, vector<XmrInput>& ins)
MySqlAccounts::select_inputs_for_out(const uint64_t& output_id,
vector<XmrInput>& ins)
{
return mysql_in->select_for_out(output_id, ins);
}
bool
MySqlAccounts::output_exists(const string& output_public_key_str, XmrOutput& out)
MySqlAccounts::output_exists(const string& output_public_key_str,
XmrOutput& out)
{
return mysql_out->exist(output_public_key_str, out);
}
bool
MySqlAccounts::tx_exists(const uint64_t& account_id, const string& tx_hash_str, XmrTransaction& tx)
MySqlAccounts::tx_exists(const uint64_t& account_id,
const string& tx_hash_str, XmrTransaction& tx)
{
return mysql_tx->exist(account_id, tx_hash_str, tx);
}
@ -591,13 +636,15 @@ MySqlAccounts::delete_tx(const uint64_t& tx_id_no)
}
bool
MySqlAccounts::select_payment_by_id(const string& payment_id, vector<XmrPayment>& payments)
MySqlAccounts::select_payment_by_id(const string& payment_id,
vector<XmrPayment>& payments)
{
return mysql_payment->select_by_payment_id(payment_id, payments);
}
bool
MySqlAccounts::get_total_recieved(const uint64_t& account_id, uint64_t& amount)
MySqlAccounts::get_total_recieved(const uint64_t& account_id,
uint64_t& amount)
{
return mysql_tx->get_total_recieved(account_id, amount);
}
@ -617,7 +664,8 @@ MySqlAccounts::get_connection()
void
MySqlAccounts::set_bc_status_provider(shared_ptr<CurrentBlockchainStatus> bc_status_provider)
MySqlAccounts::set_bc_status_provider(
shared_ptr<CurrentBlockchainStatus> bc_status_provider)
{
current_bc_status = bc_status_provider;
}

@ -5,7 +5,7 @@
#ifndef RESTBED_XMR_MYSQLACCOUNTS_H
#define RESTBED_XMR_MYSQLACCOUNTS_H
#include "tools.h"
#include "../utils.h"
#include "MySqlConnector.h"
@ -29,7 +29,6 @@ class XmrOutput;
class XmrTransaction;
class XmrPayment;
class XmrAccount;
class TxSearch;
class Table;
class CurrentBlockchainStatus;
@ -99,7 +98,6 @@ public:
select_by_payment_id(const string& payment_id, vector<XmrPayment>& payments);
};
class TxSearch;
class MySqlAccounts
{

@ -0,0 +1,80 @@
//
// Created by mwo on 12/07/18.
//
#include "easylogging++.h"
#include "../om_log.h"
#include "MysqlPing.h"
#include <algorithm>
namespace xmreg
{
MysqlPing::MysqlPing(
std::shared_ptr<MySqlConnector> _conn,
seconds _ping_time, seconds _sleep_time)
: conn {_conn}, ping_time {_ping_time},
thread_sleep_time {_sleep_time}
{}
void
MysqlPing::operator()()
{
// the while loop below is going to execute
// faster than ping_time specified. The reason
// is so that we can exit it in a timely manner
// when keep_looping becomes false
uint64_t between_ping_counter {0};
// if ping_time lower than thread_sleep_time,
// use ping_time for thread_sleep_time
thread_sleep_time = std::min(thread_sleep_time, ping_time);
// we are going to ping mysql every
// no_of_loops_between_pings iterations
// of the while loop
uint64_t no_of_loops_between_pings
= std::max<uint64_t>(1,
ping_time.count()/thread_sleep_time.count()) - 1;
while (keep_looping)
{
std::this_thread::sleep_for(thread_sleep_time);
if (++between_ping_counter <= no_of_loops_between_pings)
{
continue;
}
between_ping_counter = 0;
if (auto c = conn.lock())
{
if (!c->ping())
{
OMERROR << "Pinging mysql failed. Stoping mysql pinging thread.";
why_stoped = StopReason::PingFailed;
break;
}
OMINFO << "Mysql ping successful." ;
}
else
{
OMERROR << "std::weak_ptr<MySqlConnector> conn expired!";
why_stoped = StopReason::PointerExpired;
break;
}
++counter;
}
OMINFO << "Exiting Mysql ping thread loop.";
}
}

@ -14,13 +14,18 @@
namespace xmreg
{
using chrono::seconds;
using namespace literals::chrono_literals;
class MysqlPing
{
public:
enum class StopReason {NotYetStopped, PingFailed, PointerExpired};
MysqlPing(std::shared_ptr<MySqlConnector> _conn, uint64_t _ping_time = 300);
MysqlPing(std::shared_ptr<MySqlConnector> _conn,
seconds _ping_time = 300s,
seconds _sleep_time = 5s);
void operator()();
void stop() {keep_looping = false;}
@ -31,9 +36,12 @@ public:
MysqlPing(MysqlPing&&) = default;
MysqlPing& operator=(MysqlPing&&) = default;
~MysqlPing() = default;
private:
std::weak_ptr<MySqlConnector> conn;
uint64_t ping_time; // in seconds
seconds ping_time {300};
seconds thread_sleep_time {5};
atomic<bool> keep_looping {true};
atomic<uint64_t> counter {0};
atomic<StopReason> why_stoped {StopReason::NotYetStopped};

@ -5,7 +5,7 @@
#ifndef RESTBED_XMR_SSQLSES_H
#define RESTBED_XMR_SSQLSES_H
#include "../ext/json.hpp"
#include "ext/json.hpp"
#include <mysql++/mysql++.h>
#include <mysql++/ssqls.h>

@ -1,45 +0,0 @@
//
// Created by mwo on 5/11/15.
//
#ifndef XMREG01_MONERO_HEADERS_H_H
#define XMREG01_MONERO_HEADERS_H_H
#define DB_LMDB 2
#define BLOCKCHAIN_DB DB_LMDB
#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003"
#define SIGNED_TX_PREFIX "Monero signed tx set\003"
#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002"
#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003"
#define FEE_ESTIMATE_GRACE_BLOCKS 10
#define RECENT_OUTPUT_ZONE ((time_t)(RECENT_OUTPUT_DAYS * 86400))
#include "version.h"
#include "net/http_client.h"
#include "storages/http_abstract_invoke.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/lmdb/db_lmdb.h"
#include "wallet/wallet2.h"
#include "serialization/binary_utils.h"
#include "ringct/rctTypes.h"
#include "ringct/rctOps.h"
#include "ringct/rctSigs.h"
#include "easylogging++.h"
#include "common/base58.h"
#include "string_coding.h"
#endif //XMREG01_MONERO_HEADERS_H_H

@ -1,7 +1,7 @@
#ifndef OM_LOG_H
#define OM_LOG_H
#define OPENMONERO_LOG_CATEGORY "openmonero"
#define OPENMONERO_LOG_CATEGORY "OM"
#define OMINFO CLOG(INFO, OPENMONERO_LOG_CATEGORY)
#define OMWARN CLOG(WARNING, OPENMONERO_LOG_CATEGORY)

@ -2,7 +2,7 @@
// Created by marcin on 5/11/15.
//
#include "tools.h"
#include "utils.h"
#include <codecvt>
@ -44,6 +44,7 @@ parse_str_secret_key(const string& key_str, T& secret_key)
template bool parse_str_secret_key<crypto::secret_key>(const string& key_str, crypto::secret_key& secret_key);
template bool parse_str_secret_key<crypto::public_key>(const string& key_str, crypto::public_key& secret_key);
template bool parse_str_secret_key<crypto::hash>(const string& key_str, crypto::hash& secret_key);
template bool parse_str_secret_key<crypto::hash8>(const string& key_str, crypto::hash8& secret_key);
/**
* Get transaction tx using given tx hash. Hash is represent as string here,
@ -710,12 +711,30 @@ get_payment_id(const vector<uint8_t>& extra,
}
// just a copy from bool
// device_default::encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key)
bool
get_payment_id(const transaction& tx,
crypto::hash& payment_id,
crypto::hash8& payment_id8)
encrypt_payment_id(crypto::hash8 &payment_id,
const crypto::public_key &public_key,
const crypto::secret_key &secret_key)
{
return get_payment_id(tx.extra, payment_id, payment_id8);
#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d
crypto::key_derivation derivation;
crypto::hash hash;
char data[33]; /* A hash, and an extra byte */
if (!generate_key_derivation(public_key, secret_key, derivation))
return false;
memcpy(data, &derivation, 32);
data[32] = ENCRYPTED_PAYMENT_ID_TAIL;
cn_fast_hash(data, 33, hash);
for (size_t b = 0; b < 8; ++b)
payment_id.data[b] ^= hash.data[b];
return true;
}
@ -820,12 +839,23 @@ decode_ringct(const rct::rctSig& rv,
return false;
}
crypto::secret_key scalar1;
return decode_ringct(rv, derivation, i, mask, amount);
}
crypto::derivation_to_scalar(derivation, i, scalar1);
bool
decode_ringct(rct::rctSig const& rv,
crypto::key_derivation const& derivation,
unsigned int i,
rct::key& mask,
uint64_t& amount)
{
try
{
crypto::secret_key scalar1;
crypto::derivation_to_scalar(derivation, i, scalar1);
switch (rv.type)
{
case rct::RCTTypeSimple:
@ -844,19 +874,20 @@ decode_ringct(const rct::rctSig& rv,
hw::get_device("default"));
break;
default:
cerr << "Unsupported rct type: " << rv.type << endl;
cerr << "Unsupported rct type: " << rv.type << '\n';
return false;
}
}
catch (const std::exception &e)
catch (...)
{
cerr << "Failed to decode input " << i << endl;
cerr << "Failed to decode input " << i << '\n';
return false;
}
return true;
}
bool
url_decode(const std::string& in, std::string& out)
{
@ -924,57 +955,13 @@ parse_crow_post_data(const string& req_body)
return body;
}
// based on
// crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const
public_key
get_tx_pub_key_from_received_outs(const transaction &tx)
{
std::vector<tx_extra_field> tx_extra_fields;
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
}
// Due to a previous bug, there might be more than one tx pubkey in extra, one being
// the result of a previously discarded signature.
// For speed, since scanning for outputs is a slow process, we check whether extra
// contains more than one pubkey. If not, the first one is returned. If yes, they're
// checked for whether they yield at least one output
tx_extra_pub_key pub_key_field;
if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 0))
{
return null_pkey;
}
public_key tx_pub_key = pub_key_field.pub_key;
bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1);
if (!two_found)
{
// easy case, just one found
return tx_pub_key;
}
else
{
// just return second one if there are two.
// this does not require private view key, as
// its not needed for my use case.
return pub_key_field.pub_key;
}
return null_pkey;
}
string
xmr_amount_to_str(const uint64_t& xmr_amount, string format)
{
return fmt::format(format, XMR_AMOUNT(xmr_amount));
}
//string
//xmr_amount_to_str(const uint64_t& xmr_amount, string format)
//{
//return fmt::format(format, XMR_AMOUNT(xmr_amount));
//}

@ -13,10 +13,9 @@
#define REMOVE_HASH_BRAKETS(a_hash) \
a_hash.substr(1, a_hash.size()-2)
#include "monero_headers.h"
#include "src/monero_headers.h"
#include "../ext/format.h"
#include "../ext/json.hpp"
#include "ext/json.hpp"
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
@ -158,10 +157,32 @@ get_payment_id(const vector<uint8_t>& extra,
crypto::hash& payment_id,
crypto::hash8& payment_id8);
bool
inline bool
get_payment_id(const transaction& tx,
crypto::hash& payment_id,
crypto::hash8& payment_id8);
crypto::hash8& payment_id8)
{
return get_payment_id(tx.extra, payment_id, payment_id8);
}
inline tuple<crypto::hash, crypto::hash8>
get_payment_id(transaction const& tx)
{
crypto::hash payment_id;
crypto::hash8 payment_id8;
get_payment_id(tx.extra, payment_id, payment_id8);
return make_tuple(payment_id, payment_id8);
}
// Encryption and decryption are the same operation (xor with a key)
bool
encrypt_payment_id(crypto::hash8 &payment_id,
const crypto::public_key &public_key,
const crypto::secret_key &secret_key);
inline double
get_xmr(uint64_t core_amount)
@ -193,20 +214,21 @@ decode_ringct(const rct::rctSig & rv,
uint64_t & amount);
bool
decode_ringct(rct::rctSig const& rv,
crypto::key_derivation const& derivation,
unsigned int i,
rct::key& mask,
uint64_t& amount);
bool
url_decode(const std::string& in, std::string& out);
map<std::string, std::string>
parse_crow_post_data(const string& req_body);
// based on
// crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const
public_key
get_tx_pub_key_from_received_outs(const transaction &tx);
string
xmr_amount_to_str(const uint64_t& xmr_amount, string format="{:0.12f}");
//string
//xmr_amount_to_str(const uint64_t& xmr_amount, string format="{:0.12f}");
bool
is_output_ours(const size_t& output_index,

@ -0,0 +1 @@
Subproject commit 9562666a598b52b8f3c12cbedc73e79398f1599b

@ -3,7 +3,8 @@
macro(add_om_test _TEST_NAME)
add_executable(${_TEST_NAME}_tests
${_TEST_NAME}_tests.cpp
${_TEST_NAME}_tests.cpp
JsonTx.cpp
#${MONERO_DIR}/tests/core_tests/chaingen.cpp
#${MONERO_DIR}/tests/core_tests/chaingen001.cpp
)
@ -13,11 +14,16 @@ macro(add_om_test _TEST_NAME)
gmock gmock_main
${LIBRARIES})
target_include_monero_directories(${_TEST_NAME}_tests)
target_include_directories(${_TEST_NAME}_tests
PRIVATE ../src/xmregcore)
add_test(NAME ${_TEST_NAME}_tests COMMAND ${_TEST_NAME}_tests)
endmacro()
resource_dir("./res")
resource_dir("res")
add_om_test(mysql)
add_om_test(microcore)
@ -28,7 +34,6 @@ SETUP_TARGET_FOR_COVERAGE(
NAME mysql_cov
EXECUTABLE mysql_tests)
SETUP_TARGET_FOR_COVERAGE(
NAME microcore_cov
EXECUTABLE microcore_tests)
@ -40,3 +45,9 @@ SETUP_TARGET_FOR_COVERAGE(
SETUP_TARGET_FOR_COVERAGE(
NAME txsearch_cov
EXECUTABLE txsearch_tests)
add_custom_target(cov
COMMAND ;
DEPENDS mysql_cov microcore_cov bcstatus_cov txsearch_cov
COMMENT "Runs all coverage tests"
)

@ -0,0 +1,190 @@
#include "JsonTx.h"
namespace xmreg
{
JsonTx::JsonTx(json _jtx): jtx {std::move(_jtx)}
{
}
JsonTx::JsonTx(string _path): jpath {std::move(_path)}
{
if (!read_config())
{
throw std::runtime_error("Cant read " + jpath);
}
init();
}
void
JsonTx::get_output_tx_and_index(
uint64_t const& amount,
vector<uint64_t> const& offsets,
vector<tx_out_index>& indices) const
{
for (auto const& jinput: jtx["inputs"])
{
if (jinput["amount"] != amount
&& jinput["absolute_offsets"] != offsets)
continue;
for (auto const& jring_member: jinput["ring_members"])
{
crypto::hash tx_hash;
if (!hex_to_pod(jring_member["tx_hash"], tx_hash))
throw std::runtime_error(
"hex_to_pod(jring_member[\"tx_hash\"], tx_hash)");
indices.emplace_back(tx_hash, jring_member["output_index_in_tx"]);
}
}
}
bool
JsonTx::get_tx(crypto::hash const& tx_hash,
transaction& tx) const
{
for (auto const& jinput: jtx["inputs"])
{
for (auto const& jring_member: jinput["ring_members"])
{
if (jring_member["tx_hash"] != pod_to_hex(tx_hash))
continue;
crypto::hash tx_hash_tmp; \
crypto::hash tx_prefix_hash_tmp; \
if (!xmreg::hex_to_tx(jring_member["tx_hex"],
tx, tx_hash_tmp, tx_prefix_hash_tmp))
throw std::runtime_error(
"xmreg::hex_to_tx(jring_member[\"tx_hash\"]");
(void) tx_hash_tmp;
(void) tx_prefix_hash_tmp;
return true;
}
}
return false;
}
void
JsonTx::init()
{
ntype = cryptonote::network_type {jtx["nettype"]};
hex_to_pod(jtx["payment_id"], payment_id);
hex_to_pod(jtx["payment_id8"], payment_id8);
hex_to_pod(jtx["payment_id8e"], payment_id8e);
addr_and_viewkey_from_string(
jtx["sender"]["address"], jtx["sender"]["viewkey"], \
ntype, sender.address, sender.viewkey);
parse_str_secret_key(jtx["sender"]["spendkey"], sender.spendkey);
sender.amount = jtx["sender"]["amount"];
sender.change = jtx["sender"]["change"];
sender.ntype = ntype;
populate_outputs(jtx["sender"]["outputs"], sender.outputs);
for (auto const& jrecpient: jtx["recipient"])
{
recipients.push_back(account{});
addr_and_viewkey_from_string(
jrecpient["address"], jrecpient["viewkey"], \
ntype, recipients.back().address,
recipients.back().viewkey);
parse_str_secret_key(jrecpient["spendkey"],
recipients.back().spendkey);
recipients.back().amount = jrecpient["amount"];
recipients.back().is_subaddress = jrecpient["is_subaddress"];
recipients.back().ntype = ntype;
populate_outputs(jrecpient["outputs"], recipients.back().outputs);
}
if (!hex_to_tx(jtx["hex"], tx, tx_hash, tx_prefix_hash))
{
throw std::runtime_error("hex_to_tx(jtx[\"hex\"], tx, tx_hash, tx_prefix_hash)");
}
}
bool
JsonTx::read_config()
{
if (!boost::filesystem::exists(jpath))
{
cerr << "Config file " << jpath << " does not exist\n";
return false;
}
try
{
// try reading and parsing json config file provided
std::ifstream i(jpath);
i >> jtx;
}
catch (std::exception const& e)
{
cerr << "Cant parse json string as json: " << e.what() << endl;
return false;
}
return true;
}
bool
check_and_adjust_path(string& in_path)
{
if (!boost::filesystem::exists(in_path))
in_path = "./tests/" + in_path;
return boost::filesystem::exists(in_path);
}
boost::optional<JsonTx>
construct_jsontx(string tx_hash)
{
string tx_path = "./tx/" + tx_hash + ".json";
if (!check_and_adjust_path(tx_path))
{
return {};
}
return JsonTx {tx_path};
}
void
JsonTx::populate_outputs(json const& joutputs, vector<output>& outs)
{
for (auto const& jout: joutputs)
{
public_key out_pk;
if (!hex_to_pod(jout[1], out_pk))
{
throw std::runtime_error("hex_to_pod(jout[1], out_pk)");
}
output out {jout[0], out_pk, jout[2]};
outs.push_back(out);
}
}
}

@ -0,0 +1,109 @@
#ifndef JSONTX_H
#define JSONTX_H
#include "../src/utils.h"
namespace xmreg
{
/**
* @brief reprsents txs found in tests/res/tx folder
*
* Usef for unit testing and mocking tx data, which
* otherwise would need to be fetched from blockchain
*
*/
class JsonTx
{
public:
struct output
{
uint64_t index {0};
public_key pub_key;
uint64_t amount {0};
};
struct account
{
address_parse_info address {};
secret_key viewkey {};
secret_key spendkey {};
bool is_subaddress {false};
network_type ntype;
uint64_t amount {0};
uint64_t change {0};
vector<output> outputs;
inline string
address_str() const
{
return get_account_address_as_str(ntype,
is_subaddress,
address.address);
}
friend std::ostream&
operator<<(std::ostream& os, account const& _account);
};
json jtx;
transaction tx;
crypto::hash tx_hash {0}; \
crypto::hash tx_prefix_hash {0};
crypto::public_key tx_pub_key;
crypto::hash payment_id {0};
crypto::hash8 payment_id8 {0};
crypto::hash8 payment_id8e {0};
string jpath;
network_type ntype;
account sender;
vector<account> recipients;
explicit JsonTx(json _jtx);
explicit JsonTx(string _path);
// generate output which normaly is produced by
// monero's get_output_tx_and_index and blockchain,
// but here we use data from the tx json data file
// need this for mocking blockchain calls in unit tests
void
get_output_tx_and_index(
uint64_t const& amount,
vector<uint64_t> const& offsets,
vector<tx_out_index>& indices) const;
// will use data from the json file
// to set tx. Used for mocking this operation
// that is normaly done using blockchain
bool
get_tx(crypto::hash const& tx_hash,
transaction& tx) const;
private:
void init();
bool read_config();
void populate_outputs(json const& joutputs, vector<output>& outs);
};
inline std::ostream&
operator<<(std::ostream& os, JsonTx::account const& _account)
{
return os << _account.address_str();
}
bool
check_and_adjust_path(string& in_path);
boost::optional<JsonTx>
construct_jsontx(string tx_hash);
}
#endif // JSONTX_H

@ -22,7 +22,7 @@ information on conneting to the test database in
cd openmonero/build
# indicate that test should be build
cmake -DBUILD_TEST=ON ..
cmake -DBUILD_TESTS=ON ..
# compile openmonero with tests
make

@ -1,5 +1,5 @@
#include "../src/MicroCore.h"
#include "src/MicroCore.h"
#include "../src/CurrentBlockchainStatus.h"
#include "../src/ThreadRAII.h"
@ -14,8 +14,6 @@ namespace
class BCSTATUS_TEST : public ::testing::TestWithParam<network_type>
{
public:
@ -43,8 +41,12 @@ protected:
rpc = std::make_unique<MockRPCCalls>("dummy deamon url");
rpc_ptr = rpc.get();
tp = std::make_unique<TP::ThreadPool>();
tp_ptr = tp.get();
bcs = std::make_unique<xmreg::CurrentBlockchainStatus>(
bc_setup, std::move(mcore), std::move(rpc));
bc_setup, std::move(mcore), std::move(rpc),
std::move(tp));
}
network_type net_type {network_type::STAGENET};
@ -52,10 +54,12 @@ protected:
xmreg::BlockchainSetup bc_setup;
std::unique_ptr<MockMicroCore> mcore;
std::unique_ptr<MockRPCCalls> rpc;
std::unique_ptr<TP::ThreadPool> tp;
std::unique_ptr<xmreg::CurrentBlockchainStatus> bcs;
MockMicroCore* mcore_ptr;
MockRPCCalls* rpc_ptr;
TP::ThreadPool* tp_ptr;
static nlohmann::json config_json;
};
@ -66,7 +70,8 @@ nlohmann::json BCSTATUS_TEST::config_json;
TEST_P(BCSTATUS_TEST, DefaultConstruction)
{
xmreg::CurrentBlockchainStatus bcs {bc_setup, nullptr, nullptr};
xmreg::CurrentBlockchainStatus bcs {
bc_setup, nullptr, nullptr, nullptr};
EXPECT_TRUE(true);
}
@ -316,8 +321,8 @@ TEST_P(BCSTATUS_TEST, GetCurrentHeight)
{
uint64_t mock_current_height {1619148};
EXPECT_CALL(*mcore_ptr, get_current_blockchain_height())
.WillOnce(Return(mock_current_height));
EXPECT_CALL(*rpc_ptr, get_current_height(_))
.WillOnce(SetArgReferee<0>(mock_current_height));
bcs->update_current_blockchain_height();
@ -333,8 +338,8 @@ TEST_P(BCSTATUS_TEST, IsTxSpendtimeUnlockedScenario1)
const uint64_t mock_current_height {100};
EXPECT_CALL(*mcore_ptr, get_current_blockchain_height())
.WillOnce(Return(mock_current_height));
EXPECT_CALL(*rpc_ptr, get_current_height(_))
.WillOnce(SetArgReferee<0>(mock_current_height));
bcs->update_current_blockchain_height();
@ -366,7 +371,7 @@ TEST_P(BCSTATUS_TEST, IsTxSpendtimeUnlockedScenario1)
- CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
EXPECT_TRUE(bcs->is_tx_unlocked(tx_unlock_time,
not_used_block_height));
not_used_block_height));
// expected unlock time is same as as current height
// thus a tx is unlocked
@ -374,7 +379,7 @@ TEST_P(BCSTATUS_TEST, IsTxSpendtimeUnlockedScenario1)
tx_unlock_time = mock_current_height;
EXPECT_TRUE(bcs->is_tx_unlocked(tx_unlock_time,
not_used_block_height));
not_used_block_height));
}
@ -395,8 +400,8 @@ TEST_P(BCSTATUS_TEST, IsTxSpendtimeUnlockedScenario2)
const uint64_t mock_current_height {100};
EXPECT_CALL(*mcore_ptr, get_current_blockchain_height())
.WillOnce(Return(mock_current_height));
EXPECT_CALL(*rpc_ptr, get_current_height(_))
.WillOnce(SetArgReferee<0>(mock_current_height));
bcs->update_current_blockchain_height();
@ -1496,7 +1501,7 @@ TEST_P(BCSTATUS_TEST, MonitorBlockchain)
// set refresh rate to 1 second as we dont wont to wait long
xmreg::BlockchainSetup bs = bcs->get_bc_setup();
bs.refresh_block_status_every_seconds = 1;
bs.refresh_block_status_every = 1s;
bcs->set_bc_setup(bs);
bcs->is_running = false;

@ -1,5 +1,7 @@
#pragma once
#include "src/UniversalIdentifier.hpp"
#include <string>
#define RAND_TX_HASH() \
@ -33,6 +35,35 @@
address_str, viewkey_str, \
net_type, address, viewkey));
#define SPENDKEY_FROM_STRING(spendkey_str) \
crypto::secret_key spendkey; \
ASSERT_TRUE(xmreg::parse_str_secret_key(spendkey_str, spendkey));
#define TX_AND_ADDR_VIEWKEY_FROM_STRING(hex_tx, address_str, viewkey_str, net_type) \
TX_FROM_HEX(hex_tx); \
ADDR_VIEWKEY_FROM_STRING(address_str, viewkey_str, net_type);
#define GET_RING_MEMBER_OUTPUTS(ring_member_data_hex) \
MockGettingOutputs::ring_members_mock_map_t mock_ring_members_data; \
ASSERT_TRUE(xmreg::output_data_from_hex(ring_member_data_hex, \
mock_ring_members_data)); \
MockGettingOutputs get_outputs(mock_ring_members_data);
#define GET_KNOWN_OUTPUTS(known_outputs_csv) \
Input::known_outputs_t known_outputs; \
ASSERT_TRUE(check_and_adjust_path(known_outputs_csv)); \
ASSERT_TRUE(xmreg::populate_known_outputs_from_csv( \
known_outputs_csv, known_outputs));
#define MOCK_MCORE_GET_OUTPUT_KEY() \
auto mcore = std::make_unique<MockMicroCore>(); \
EXPECT_CALL(*mcore, get_output_key(_,_,_)) \
.WillRepeatedly(Invoke(&get_outputs, \
&MockGettingOutputs \
::get_output_key));

@ -2,7 +2,7 @@
// Created by mwo on 15/06/18.
//
#include "../src/MicroCore.h"
#include "src/MicroCore.h"
#include "../src/BlockchainSetup.h"

@ -1,14 +1,14 @@
#pragma once
#include "../src/MicroCore.h"
#include "src/MicroCore.h"
#include "../src/CurrentBlockchainStatus.h"
#include "../src/ThreadRAII.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "JsonTx.h"
namespace
{
@ -48,8 +48,8 @@ public:
bool(uint64_t height, block& blk));
MOCK_CONST_METHOD2(get_blocks_range,
std::vector<block>(const uint64_t& h1,
const uint64_t& h2));
std::vector<block>(uint64_t h1,
uint64_t h2));
MOCK_CONST_METHOD3(get_transactions,
bool(const std::vector<crypto::hash>& txs_ids,
@ -66,24 +66,29 @@ public:
uint64_t& tx_id));
MOCK_CONST_METHOD2(get_output_tx_and_index,
tx_out_index(uint64_t const& amount,
uint64_t const& index));
tx_out_index(uint64_t amount,
uint64_t index));
MOCK_CONST_METHOD3(get_output_tx_and_index,
void(uint64_t amount,
const std::vector<uint64_t> &offsets,
std::vector<tx_out_index> &indices));
MOCK_CONST_METHOD2(get_tx,
bool(crypto::hash const& tx_hash,
transaction& tx));
MOCK_METHOD3(get_output_key,
void(const uint64_t& amount,
const vector<uint64_t>& absolute_offsets,
vector<cryptonote::output_data_t>& outputs));
void(uint64_t amount,
vector<uint64_t> const& absolute_offsets,
vector<output_data_t>& outputs));
MOCK_METHOD2(get_output_key,
output_data_t(uint64_t amount,
uint64_t global_amount_index));
MOCK_CONST_METHOD1(get_tx_amount_output_indices,
std::vector<uint64_t>(uint64_t const& tx_id));
std::vector<uint64_t>(uint64_t tx_id));
MOCK_CONST_METHOD2(get_random_outs_for_amounts,
bool(COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request const& req,
@ -94,7 +99,7 @@ public:
COMMAND_RPC_GET_OUTPUTS_BIN::response& res));
MOCK_CONST_METHOD1(get_dynamic_base_fee_estimate,
uint64_t(uint64_t const& grace_blocks));
uint64_t(uint64_t grace_blocks));
MOCK_CONST_METHOD2(get_mempool_txs,
bool(vector<tx_info>& tx_infos,
@ -118,6 +123,9 @@ public:
MOCK_METHOD3(commit_tx, bool(const string& tx_blob,
string& error_msg,
bool do_not_relay));
MOCK_METHOD1(get_current_height,
bool(uint64_t& current_height));
};
class MockTxSearch : public xmreg::TxSearch
@ -172,7 +180,7 @@ class MockCurrentBlockchainStatus : public xmreg::CurrentBlockchainStatus
public:
MockCurrentBlockchainStatus()
: xmreg::CurrentBlockchainStatus(xmreg::BlockchainSetup(),
nullptr, nullptr)
nullptr, nullptr, nullptr)
{}
MOCK_METHOD3(get_output_keys,
@ -209,7 +217,7 @@ struct MockGettingOutputs
// based on absolute_offsets
virtual bool
get_output_keys(
const uint64_t& amount,
uint64_t amount,
vector<uint64_t> absolute_offsets,
vector<cryptonote::output_data_t>& outputs)
{
@ -225,15 +233,19 @@ struct MockGettingOutputs
return true;
}
};
bool
check_and_adjust_path(string& in_path)
{
if (!boost::filesystem::exists(in_path))
in_path = "./tests/" + in_path;
/*
* MicroCore uses get_output_key, so provide this for convinience
* which is just same as get_output_keys above
*/
virtual void
get_output_key(
uint64_t amount,
vector<uint64_t> absolute_offsets,
vector<cryptonote::output_data_t>& outputs)
{
get_output_keys(amount, absolute_offsets, outputs);
}
return boost::filesystem::exists(in_path);
}
};
}

@ -2,9 +2,9 @@
// Created by mwo on 15/06/18.
//
#include "../src/MicroCore.h"
#include "../src/YourMoneroRequests.h"
#include "../src/MysqlPing.h"
#include "src/MicroCore.h"
#include "../src/OpenMoneroRequests.h"
#include "../src/db/MysqlPing.h"
//#include "chaingen.h"
//#include "chaingen_tests_list.h"
@ -118,7 +118,7 @@ TEST(MYSQL_CONNECTION, CanConnect)
json db_config = readin_config();
if (db_config.empty())
FAIL() << "Cant readin_config()";
FAIL() << "Cant read in_config()";
xmreg::MySqlConnector::url = db_config["url"];
xmreg::MySqlConnector::port = db_config["port"];
@ -159,7 +159,7 @@ public:
db_config = readin_config();
if (db_config.empty())
FAIL() << "Cant readin_config()";
FAIL() << "Cant read in_config()";
xmreg::MySqlConnector::url = db_config["url"];
xmreg::MySqlConnector::port = db_config["port"];
@ -336,6 +336,8 @@ TEST_F(MYSQL_TEST, InsertAndGetAccount)
bool is_success = xmr_accounts->select(xmr_addr, acc);
ASSERT_TRUE(is_success);
EXPECT_EQ(acc.id.data, expected_primary_id);
EXPECT_EQ(acc.scanned_block_height, mock_current_blockchain_height);
EXPECT_EQ(acc.scanned_block_timestamp, mock_current_blockchain_timestamp);
@ -1100,7 +1102,7 @@ make_mock_payment_data(string last_char_pub_key = "0")
mock_data.import_fee = 10000000010ull; // xmr
mock_data.request_fulfilled = false;
mock_data.tx_hash = ""; // no tx_hash yet with the payment
mock_data.payment_address = "5DUWE29P72Eb8inMa41HuNJG4tj9CcaNKGr6EVSbvhWGJdpDQCiNNYBUNF1oDb8BczU5aD68d3HNKXaEsPq8cvbQLK4Tiiy";
mock_data.payment_address = "5DUWE29P72Eb8inMa41HuNJG4tj9CcaNKGr6EVSbvhWGJdpDQCiNNYBUNF1oDb8BczU5aD68d3HNKXaEsPq8cvbQLGi6vcb2zkW7mhsWor";
return mock_data;
}
@ -1197,7 +1199,7 @@ public:
MockCurrentBlockchainStatus1()
: xmreg::CurrentBlockchainStatus(
xmreg::BlockchainSetup(),
nullptr, nullptr)
nullptr, nullptr, nullptr)
{}
bool tx_unlock_state {true};
@ -1502,7 +1504,7 @@ TEST_F(MYSQL_TEST, MysqlPingThreadStopsOnPingFailure)
auto conn = xmr_accounts->get_connection();
// create ping functor that will be pinging mysql every 1 second
xmreg::MysqlPing mysql_ping {conn, 1};
xmreg::MysqlPing mysql_ping {conn, 1s};
{
// create ping thread and start pinging
@ -1543,7 +1545,7 @@ TEST_F(MYSQL_TEST, MysqlPingThreadStopsOnPointerExpiry)
ASSERT_TRUE(new_conn->get_connection().connected());
// create ping functor that will be pinging mysql every 1 second
xmreg::MysqlPing mysql_ping {new_conn, 1};
xmreg::MysqlPing mysql_ping {new_conn, 1s};
{
// create ping thread and start pinging

@ -0,0 +1,7 @@
# Test transaction data
Json files containing testing transactions data for unit tests.
The were obtained using `Full ring member txs as hex` option in the onion explorer
running wiht `--enable-as-hex` flag.
The wallet information were manual entered.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,33 @@
{
"_comment": "Just a placeholder for some comment if needed later",
"block": "0111010101010201010405626c6f636b0a050209098aab83e005e094b83d50e762b943df3158e9a87bc49f75ab9f2e800be380bd92bbaae477e84ac9844a02d08e0d01ff948e0d01b7de97a0f0bc0502472b5dcf3d976fa1b8e0d250a7c4e9dc52ae9db7b8fb00934f9dac800da9f89f2101b9e281d63f319b30ea87d789d324cbce8b5edfa6a8e00f001f8449d8ba2b71fe0000",
"block_version": [9, 9],
"hash": "f3c84fe925292ec5b4dc383d306d934214f4819611566051bca904d1cf4efceb",
"hex": "02d08e0d01ff948e0d01b7de97a0f0bc0502472b5dcf3d976fa1b8e0d250a7c4e9dc52ae9db7b8fb00934f9dac800da9f89f2101b9e281d63f319b30ea87d789d324cbce8b5edfa6a8e00f001f8449d8ba2b71fe00",
"inputs": [],
"is_ringct": true,
"nettype": 2,
"payment_id": "0000000000000000000000000000000000000000000000000000000000000000",
"payment_id8": "0000000000000000",
"payment_id8e": "0000000000000000",
"rct_type": 0,
"recipient": [{
"_comment": "monerowalletstagenet3, coinbase tx",
"address": "56heRv2ANffW1Py2kBkJDy8xnWqZsSrgjLygwjua2xc8Wbksead1NK1ehaYpjQhymGK4S8NPL9eLuJ16CuEJDag8Hq3RbPV",
"amount": 24081949126455,
"is_subaddress": false,
"seed": "",
"spendkey": "df0f5720ae0b69454ca7db35db677272c7c19513cd0dc4147b0e00792a10f406",
"viewkey": "b45e6f38b2cd1c667459527decb438cdeadf9c64d93c8bccf40a9bf98943dc09",
"outputs": [[0, "472b5dcf3d976fa1b8e0d250a7c4e9dc52ae9db7b8fb00934f9dac800da9f89f", 24081949126455]]
}],
"sender": {
"_comment": "",
"address": "",
"amount": 0,
"change": 0,
"seed": "",
"spendkey": "",
"viewkey": ""
}
}

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save