Initial commit

dockerfile
tobtoht 4 years ago
commit 1be50e306a

2
.gitignore vendored

@ -0,0 +1,2 @@
cmake-build-debug/
.idea/

@ -0,0 +1,221 @@
cmake_minimum_required(VERSION 3.13)
project(moneroservice)
message(STATUS "Initiating compile using CMake ${CMAKE_VERSION}")
set(THREADS_PREFER_PTHREAD_FLAG ON)
set(VERSION_MAJOR "0")
set(VERSION_MINOR "1")
set(VERSION_REVISION "0")
set(VERSION "beta-3")
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
include(CheckLinkerFlag)
include(FetchContent)
include(FindCcache)
include(CheckIncludeFile)
include(CheckSymbolExists)
if(DEBUG)
set(CMAKE_VERBOSE_MAKEFILE ON)
endif()
set(ARCH "x86-64")
set(BUILD_64 ON)
set(USE_SINGLE_BUILDDIR ON)
check_include_file(sys/prctl.h HAVE_SYS_PRCTL_H)
check_symbol_exists(prctl "sys/prctl.h" HAVE_PRCTL)
function (add_c_flag_if_supported flag var)
string(REPLACE "-" "_" supported ${flag}_c)
check_c_compiler_flag(${flag} ${supported})
if(${${supported}})
set(${var} "${${var}} ${flag}" PARENT_SCOPE)
endif()
endfunction()
function (add_cxx_flag_if_supported flag var)
string(REPLACE "-" "_" supported ${flag}_cxx)
check_cxx_compiler_flag(${flag} ${supported})
if(${${supported}})
set(${var} "${${var}} ${flag}" PARENT_SCOPE)
endif()
endfunction()
function (add_linker_flag_if_supported flag var)
string(REPLACE "-" "_" supported ${flag}_ld)
string(REPLACE "," "_" supported ${flag}_ld)
check_linker_flag(${flag} ${supported})
if(${${supported}})
set(${var} "${${var}} ${flag}" PARENT_SCOPE)
endif()
endfunction()
include(CMakePackageConfigHelpers)
if(UNIX AND NOT APPLE)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
# https://github.com/monero-project/monero-gui/issues/3142#issuecomment-705940446
set(CMAKE_SKIP_RPATH ON)
endif()
find_package(X11 REQUIRED)
message(STATUS "X11_FOUND = ${X11_FOUND}")
message(STATUS "X11_INCLUDE_DIR = ${X11_INCLUDE_DIR}")
message(STATUS "X11_LIBRARIES = ${X11_LIBRARIES}")
include_directories(${X11_INCLUDE_DIR})
link_directories(${X11_LIBRARIES})
if(STATIC)
find_library(XCB_LIBRARY xcb)
message(STATUS "Found xcb library: ${XCB_LIBRARY}")
endif()
endif()
if(MINGW)
string(REGEX MATCH "^[^/]:/[^/]*" msys2_install_path "${CMAKE_C_COMPILER}")
message(STATUS "MSYS location: ${msys2_install_path}")
set(CMAKE_INCLUDE_PATH "${msys2_install_path}/mingw${ARCH_WIDTH}/include")
# This is necessary because otherwise CMake will make Boost libraries -lfoo
# rather than a full path. Unfortunately, this makes the shared libraries get
# linked due to a bug in CMake which misses putting -static flags around the
# -lfoo arguments.
set(DEFLIB ${msys2_install_path}/mingw${ARCH_WIDTH}/lib)
list(REMOVE_ITEM CMAKE_C_IMPLICIT_LINK_DIRECTORIES ${DEFLIB})
list(REMOVE_ITEM CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES ${DEFLIB})
endif()
if(MINGW)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj")
set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt)
if(DEPENDS)
set(ICU_LIBRARIES iconv)
else()
set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv)
endif()
elseif(APPLE)
set(EXTRA_LIBRARIES "-framework AppKit")
elseif(OPENBSD)
set(EXTRA_LIBRARIES "")
elseif(FREEBSD)
set(EXTRA_LIBRARIES execinfo)
elseif(DRAGONFLY)
find_library(COMPAT compat)
set(EXTRA_LIBRARIES execinfo ${COMPAT})
elseif(CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)")
set(EXTRA_LIBRARIES socket nsl resolv)
elseif(NOT MSVC AND NOT DEPENDS)
find_library(RT rt)
set(EXTRA_LIBRARIES ${RT})
endif()
list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
if(APPLE)
include_directories(SYSTEM /usr/include/malloc)
if(POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -fvisibility=default -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0")
endif()
if (APPLE AND NOT IOS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -fvisibility=default -std=c++11")
endif()
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0")
endif()
# warnings
# @TODO: enable these 2 for migration to Qt 6
#add_c_flag_if_supported(-Werror C_SECURITY_FLAGS)
#add_cxx_flag_if_supported(-Werror CXX_SECURITY_FLAGS)
add_c_flag_if_supported(-Wformat C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-Wformat CXX_SECURITY_FLAGS)
add_c_flag_if_supported(-Wformat-security C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-Wformat-security CXX_SECURITY_FLAGS)
# -fstack-protector
if (NOT OPENBSD AND NOT (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1)))
add_c_flag_if_supported(-fstack-protector C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-fstack-protector CXX_SECURITY_FLAGS)
add_c_flag_if_supported(-fstack-protector-strong C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-fstack-protector-strong CXX_SECURITY_FLAGS)
endif()
# New in GCC 8.2
if (NOT OPENBSD AND NOT (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1)))
add_c_flag_if_supported(-fcf-protection=full C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-fcf-protection=full CXX_SECURITY_FLAGS)
endif()
if (NOT WIN32 AND NOT OPENBSD)
add_c_flag_if_supported(-fstack-clash-protection C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-fstack-clash-protection CXX_SECURITY_FLAGS)
endif()
# Removed in GCC 9.1 (or before ?), but still accepted, so spams the output
if (NOT (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1))
add_c_flag_if_supported(-mmitigate-rop C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-mmitigate-rop CXX_SECURITY_FLAGS)
endif()
# linker
if (APPLE)
add_linker_flag_if_supported(-Wl,-bind_at_load LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,-dead_strip LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,-dead_strip_dylibs LD_SECURITY_FLAGS)
endif()
if (NOT APPLE AND NOT (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU"))
# Windows binaries die on startup with PIE when compiled with GCC
add_linker_flag_if_supported(-pie LD_SECURITY_FLAGS)
endif()
add_linker_flag_if_supported(-Wl,-z,relro LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,-z,now LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,-z,noexecstack noexecstack_SUPPORTED)
if (noexecstack_SUPPORTED)
set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecstack")
endif()
add_linker_flag_if_supported(-Wl,-z,noexecheap noexecheap_SUPPORTED)
if (noexecheap_SUPPORTED)
set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap")
endif()
# some windows linker bits
if (WIN32)
add_linker_flag_if_supported(-Wl,--dynamicbase LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,--nxcompat LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,--high-entropy-va LD_SECURITY_FLAGS)
endif()
if(STATIC)
add_linker_flag_if_supported(-static-libgcc STATIC_FLAGS)
add_linker_flag_if_supported(-static-libstdc++ STATIC_FLAGS)
if(MINGW)
add_linker_flag_if_supported(-static STATIC_FLAGS)
endif()
endif()
# With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that
# is fixed in the code (Issue #847), force compiler to be conservative.
add_c_flag_if_supported(-fno-strict-aliasing C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-fno-strict-aliasing CXX_SECURITY_FLAGS)
add_c_flag_if_supported(-fPIC C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-fPIC CXX_SECURITY_FLAGS)
message(STATUS "Using C security hardening flags: ${C_SECURITY_FLAGS}")
message(STATUS "Using C++ security hardening flags: ${CXX_SECURITY_FLAGS}")
message(STATUS "Using linker security hardening flags: ${LD_SECURITY_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${C_SECURITY_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${CXX_SECURITY_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}")
add_subdirectory(src)

@ -0,0 +1,14 @@
#ifdef __CLASSIC_C__
int main()
{
int ac;
char* av[];
#else
int main(int ac, char* av[])
{
#endif
if (ac > 1000) {
return *av[0];
}
return 0;
}

@ -0,0 +1,47 @@
include(CheckCCompilerFlag)
macro(CHECK_LINKER_FLAG flag VARIABLE)
if(NOT DEFINED "${VARIABLE}")
if(NOT CMAKE_REQUIRED_QUIET)
message(STATUS "Looking for ${flag} linker flag")
endif()
set(_cle_source ${CMAKE_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
set(saved_CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
set(CMAKE_C_FLAGS "${flag}")
try_compile(${VARIABLE}
${CMAKE_BINARY_DIR}
${_cle_source}
COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${flag}
CMAKE_FLAGS
OUTPUT_VARIABLE OUTPUT)
unset(_cle_source)
set(CMAKE_C_FLAGS ${saved_CMAKE_C_FLAGS})
unset(saved_CMAKE_C_FLAGS)
if ("${OUTPUT}" MATCHES "warning.*ignored")
set(${VARIABLE} 0)
endif()
if(${VARIABLE})
if(NOT CMAKE_REQUIRED_QUIET)
message(STATUS "Looking for ${flag} linker flag - found")
endif()
set(${VARIABLE} 1 CACHE INTERNAL "Have linker flag ${flag}")
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
"Determining if the ${flag} linker flag is supported "
"passed with the following output:\n"
"${OUTPUT}\n\n")
else()
if(NOT CMAKE_REQUIRED_QUIET)
message(STATUS "Looking for ${flag} linker flag - not found")
endif()
set(${VARIABLE} "" CACHE INTERNAL "Have linker flag ${flag}")
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
"Determining if the ${flag} linker flag is supported "
"failed with the following output:\n"
"${OUTPUT}\n\n")
endif()
endif()
endmacro()

@ -0,0 +1,56 @@
# Copyright (c) 2014-2020, The Monero Project
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of
# conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
# of conditions and the following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# - Try to find readline include dirs and libraries
#
# Automatically finds ccache build accelerator, if it's found in PATH.
#
# Usage of this module as follows:
#
# project(monero)
# include(FindCcache) # Include AFTER the project() macro to be able to reach the CMAKE_CXX_COMPILER variable
#
# Properties modified by this module:
#
# GLOBAL PROPERTY RULE_LAUNCH_COMPILE set to ccache, when ccache found
# GLOBAL PROPERTY RULE_LAUNCH_LINK set to ccache, when ccache found
find_program(CCACHE_FOUND ccache)
if (CCACHE_FOUND)
set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp")
file(WRITE "${TEMP_CPP_FILE}" "int main() { return 0; }")
execute_process(COMMAND "${CCACHE_FOUND}" "${CMAKE_CXX_COMPILER}" "${TEMP_CPP_FILE}" RESULT_VARIABLE RET)
if (${RET} EQUAL 0)
message("found usable ccache: ${CCACHE_FOUND}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_FOUND}")
else()
message("found ccache ${CCACHE_FOUND}, but is UNUSABLE! Return code: ${RET}")
endif()
else()
message("ccache NOT found!")
endif()

@ -0,0 +1,68 @@
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# pthread
find_package(Threads REQUIRED)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui)
qt5_add_resources(RESOURCES assets.qrc)
# Compile source files (.h/.cpp)
file(GLOB SOURCE_FILES
"*.h"
"*.cpp"
"ui/BreezeStyleSheets/breeze.qrc"
"ui/qdarkstyle/style.qrc"
)
set(EXECUTABLE_FLAG)
add_executable(moneroservice ${EXECUTABLE_FLAG} main.cpp
${SOURCE_FILES}
${RESOURCES}
)
set_property(TARGET moneroservice PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
target_include_directories(moneroservice PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
file(GLOB_RECURSE SRC_SOURCES *.cpp)
file(GLOB_RECURSE SRC_HEADERS *.h)
target_include_directories(moneroservice PUBLIC
${CMAKE_BINARY_DIR}/src/moneroservice_autogen/include
${CMAKE_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}
${X11_INCLUDE_DIR}
${Qt5Core_INCLUDE_DIRS}
${Qt5Widgets_INCLUDE_DIRS}
${Qt5Gui_INCLUDE_DIRS}
)
target_compile_definitions(moneroservice
PUBLIC
${Qt5Core_DEFINITIONS}
${Qt5Widgets_DEFINITIONS}
${Qt5Gui_DEFINITIONS}
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
target_link_libraries(moneroservice
${CMAKE_DL_LIBS}
${EXTRA_LIBRARIES}
Qt5::Core
Qt5::Widgets
Qt5::Gui
${ICU_LIBRARIES}
Threads::Threads
)
if(X11_FOUND)
target_link_libraries(moneroservice ${X11_LIBRARIES})
endif()
install(TARGETS moneroservice
DESTINATION ${CMAKE_INSTALL_PREFIX}
)

@ -0,0 +1,61 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2021, The Monero Project.
#include "CommandLineEdit.h"
#include <QKeyEvent>
CommandLineEdit::CommandLineEdit(QWidget *parent) : QLineEdit(parent)
{
connect(this, &QLineEdit::returnPressed, this, &CommandLineEdit::updateHistory);
}
void CommandLineEdit::keyPressEvent(QKeyEvent *event) {
if (event->key() == Qt::Key_Up) {
if (m_historyIndex == m_history.size()) {
m_currentLine = this->text();
}
if (this->decreaseIndex()) {
this->setText(this->getCommand(m_historyIndex));
}
} else if (event->key() == Qt::Key_Down) {
if (this->increaseIndex()) {
this->setText(this->getCommand(m_historyIndex));
}
}
QLineEdit::keyPressEvent(event);
}
bool CommandLineEdit::decreaseIndex() {
if (m_historyIndex > 0 && m_history.size() >= m_historyIndex) {
m_historyIndex -= 1;
return true;
}
return false;
}
bool CommandLineEdit::increaseIndex() {
if (m_historyIndex < m_history.size()) {
m_historyIndex += 1;
return true;
}
return false;
}
QString CommandLineEdit::getCommand(int index) {
if (index < m_history.size()) {
return m_history[index];
} else {
return m_currentLine;
}
}
void CommandLineEdit::updateHistory() {
QString command = this->text();
if (m_history.isEmpty() || m_history.last() != command) {
m_history << command;
m_historyIndex = m_history.size();
}
this->setText("");
emit commandExecuted(command.split(" "));
}

@ -0,0 +1,33 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2021, The Monero Project.
#ifndef MONEROSERVICE_COMMANDLINEEDIT_H
#define MONEROSERVICE_COMMANDLINEEDIT_H
#include <QObject>
#include <QLineEdit>
class CommandLineEdit : public QLineEdit {
Q_OBJECT
public:
explicit CommandLineEdit(QWidget *parent = nullptr);
void keyPressEvent(QKeyEvent *event) override;
signals:
void commandExecuted(const QStringList &command);
private:
void updateHistory();
bool decreaseIndex();
bool increaseIndex();
QString getCommand(int index);
QString m_currentLine;
int m_historyIndex = 1;
QStringList m_history;
};
#endif //MONEROSERVICE_COMMANDLINEEDIT_H

@ -0,0 +1,118 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.
// Copyright (c) 2020 KeePassXC Team <team@keepassxc.org>
// Copyright (c) 2011 Felix Geyer <debfx@fobos.de>
#include "Config.h"
#include <QDir>
#include <QCoreApplication>
#define QS QStringLiteral
struct ConfigDirective
{
QString name;
QVariant defaultValue;
};
static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::network,{QS("network"), "mainnet"}},
{Config::pathToMonerod,{QS("pathToMonerod"), ""}},
{Config::pruneBlockchain,{QS("pruneBlockchain"), true}},
{Config::dataDir,{QS("dataDir"), ""}},
{Config::logLevel,{QS("logLevel"), 0}},
{Config::limitRateDown,{QS("limitRateDown"), 0}},
{Config::customStartupFlags,{QS("customStartupFlags"), ""}},
{Config::noZeroMQ,{QS("noZeroMQ"), true}},
{Config::RPCBindIP,{QS("RPCBindIP"), ""}},
{Config::RPCBindPort,{QS("RPCBindPort"), 0}},
{Config::P2PBindIP,{QS("P2PBindIP"), ""}},
{Config::P2PBindPort,{QS("P2PBindPort"), 0}},
};
QPointer<Config> Config::m_instance(nullptr);
QVariant Config::get(ConfigKey key)
{
auto cfg = configStrings[key];
auto defaultValue = configStrings[key].defaultValue;
return m_settings->value(cfg.name, defaultValue);
}
QString Config::getFileName()
{
return m_settings->fileName();
}
void Config::set(ConfigKey key, const QVariant& value)
{
if (get(key) == value) {
return;
}
auto cfg = configStrings[key];
m_settings->setValue(cfg.name, value);
this->sync();
emit changed(key);
}
/**
* Sync configuration with persistent storage.
*
* Usually, you don't need to call this method manually, but if you are writing
* configurations after an emitted \link QCoreApplication::aboutToQuit() signal,
* use it to guarantee your config values are persisted.
*/
void Config::sync()
{
m_settings->sync();
}
void Config::resetToDefaults()
{
m_settings->clear();
}
Config::Config(const QString& fileName, QObject* parent)
: QObject(parent)
{
init(fileName);
}
Config::Config(QObject* parent)
: QObject(parent)
{
QString configPath;
configPath = QDir::homePath();
configPath += "/.config/moneroservice/settings.json";
init(QDir::toNativeSeparators(configPath));
}
Config::~Config()
{
}
void Config::init(const QString& configFileName)
{
// const QSettings::Format jsonFormat = QSettings::registerFormat("json", Utils::readJsonFile, Utils::writeJsonFile);
// QSettings::setDefaultFormat(jsonFormat);
m_settings.reset(new QSettings());
connect(qApp, &QCoreApplication::aboutToQuit, this, &Config::sync);
}
Config* Config::instance()
{
if (!m_instance) {
m_instance = new Config(qApp);
}
return m_instance;
}

@ -0,0 +1,64 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.
// Copyright (c) 2020 KeePassXC Team <team@keepassxc.org>
// Copyright (c) 2011 Felix Geyer <debfx@fobos.de>
#ifndef MONEROSERVICE_SETTINGS_H
#define MONEROSERVICE_SETTINGS_H
#include <QObject>
#include <QSettings>
#include <QPointer>
class Config : public QObject
{
Q_OBJECT
public:
Q_DISABLE_COPY(Config)
enum ConfigKey
{
network,
pathToMonerod,
pruneBlockchain,
dataDir,
logLevel,
limitRateDown,
customStartupFlags,
noZeroMQ,
RPCBindIP,
RPCBindPort,
P2PBindIP,
P2PBindPort
};
~Config() override;
QVariant get(ConfigKey key);
QString getFileName();
void set(ConfigKey key, const QVariant& value);
void sync();
void resetToDefaults();
static Config* instance();
signals:
void changed(ConfigKey key);
private:
Config(const QString& fileName, QObject* parent = nullptr);
explicit Config(QObject* parent);
void init(const QString& configFileName);
static QPointer<Config> m_instance;
QScopedPointer<QSettings> m_settings;
QHash<QString, QVariant> m_defaults;
};
inline Config* config()
{
return Config::instance();
}
#endif //MONEROSERVICE_SETTINGS_H

@ -0,0 +1,86 @@
#include "Configurator.h"
#include "ui_Configurator.h"
#include "Config.h"
#include <QIcon>
Configurator::Configurator(QWidget *parent) :
QDialog(parent),
ui(new Ui::Configurator)
{
ui->setupUi(this);
// Basic settings
ui->lineMonerod->setText(config()->get(Config::pathToMonerod).toString());
ui->lineMonerod->setCursorPosition(0);
connect(ui->lineMonerod, &QLineEdit::textChanged, [this](const QString &text){
this->setConfig(Config::pathToMonerod, text);
});
ui->checkPruneBlockchain->setChecked(config()->get(Config::pruneBlockchain).toBool());
connect(ui->checkPruneBlockchain, &QCheckBox::toggled, [this](bool toggled){
this->setConfig(Config::pruneBlockchain, toggled);
});
ui->lineDataDir->setText(config()->get(Config::dataDir).toString());
connect(ui->lineDataDir, &QLineEdit::textChanged, [this](const QString &text){
this->setConfig(Config::dataDir, text);
});
ui->spinLimitSpeedDown->setValue(config()->get(Config::limitRateDown).toInt());
connect(ui->spinLimitSpeedDown, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value){
this->setConfig(Config::limitRateDown, value);
});
// Advanced settings
ui->comboNetwork->setCurrentIndex(config()->get(Config::network).toInt());
connect(ui->comboNetwork, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int value){
this->setConfig(Config::network, value);
});
ui->comboLogLevel->setCurrentIndex(config()->get(Config::logLevel).toInt());
connect(ui->comboLogLevel, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int value){
this->setConfig(Config::logLevel, value);
});
ui->checkNoZeroMQ->setChecked(config()->get(Config::noZeroMQ).toBool());
connect(ui->checkNoZeroMQ, &QCheckBox::toggled, [this](bool toggled){
this->setConfig(Config::noZeroMQ, toggled);
});
ui->lineRPCBindIP->setText(config()->get(Config::RPCBindIP).toString());
connect(ui->lineRPCBindIP, &QLineEdit::textChanged, [this](const QString &text){
this->setConfig(Config::RPCBindIP, text);
});
ui->lineRPCBindPort->setText(config()->get(Config::RPCBindPort).toString());
connect(ui->lineP2PBindPort, &QLineEdit::textChanged, [this](const QString &text){
this->setConfig(Config::P2PBindPort, text);
});
ui->lineP2PBindIP->setText(config()->get(Config::P2PBindIP).toString());
connect(ui->lineP2PBindIP, &QLineEdit::textChanged, [this](const QString &text){
this->setConfig(Config::P2PBindIP, text);
});
ui->lineP2PBindPort->setText(config()->get(Config::P2PBindPort).toString());
connect(ui->lineP2PBindPort, &QLineEdit::textChanged, [this](const QString &text){
this->setConfig(Config::P2PBindPort, text);
});
this->adjustSize();
}
bool Configurator::configUpdated() const {
return m_configUpdated;
}
void Configurator::setConfig(Config::ConfigKey configKey, const QVariant &value) {
this->m_configUpdated = true;
config()->set(configKey, value);
}
Configurator::~Configurator()
{
delete ui;
}

@ -0,0 +1,27 @@
#ifndef MONEROSERVICE_CONFIGURATOR_H
#define MONEROSERVICE_CONFIGURATOR_H
#include <QDialog>
#include "Config.h"
namespace Ui {
class Configurator;
}
class Configurator : public QDialog
{
Q_OBJECT
public:
explicit Configurator(QWidget *parent = nullptr);
~Configurator() override;
bool configUpdated() const;
private:
void setConfig(Config::ConfigKey configKey, const QVariant &value);
Ui::Configurator *ui;
bool m_configUpdated = false;
};
#endif //MONEROSERVICE_CONFIGURATOR_H

@ -0,0 +1,345 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Configurator</class>
<widget class="QDialog" name="Configurator">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>692</width>
<height>585</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Config</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Basic</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Path to monerod:</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="lineMonerod"/>
</item>
<item>
<widget class="QPushButton" name="btn_BrowseMonerodPath">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Blockchain</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QCheckBox" name="checkPruneBlockchain">
<property name="text">
<string>Prune blockchain</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Data directory:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="lineDataDir">
<property name="placeholderText">
<string>Leave blank for default</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_BrowseDataDir">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_7">
<property name="title">
<string>Network</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Limit download speed:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinLimitSpeedDown">
<property name="maximum">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Advanced</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Network</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboNetwork">
<item>
<property name="text">
<string>Mainnet</string>
</property>
</item>
<item>
<property name="text">
<string>Stagenet</string>
</property>
</item>
<item>
<property name="text">
<string>Testnet</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Log level</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboLogLevel">
<item>
<property name="text">
<string>Default</string>
</property>
</item>
<item>
<property name="text">
<string>Info</string>
</property>
</item>
<item>
<property name="text">
<string>Debug</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>RPC port</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>P2P port</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>RPC bind IP</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineRPCBindIP">
<property name="placeholderText">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>P2P bind IP</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="lineP2PBindIP">
<property name="placeholderText">
<string>127.0.0.1</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="lineRPCBindPort">
<property name="placeholderText">
<string>18081</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="lineP2PBindPort">
<property name="placeholderText">
<string>18082</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkNoZeroMQ">
<property name="text">
<string>No ZeroMQ</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Startup flags</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="lineStartupFlags"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>81</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Configurator</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Configurator</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

@ -0,0 +1,240 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2014-2021, The Monero Project.
#include "DaemonManager.h"
#include <QElapsedTimer>
#include <QFile>
#include <QMutexLocker>
#include <QThread>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QUrl>
#include <QtConcurrent/QtConcurrent>
#include <QApplication>
#include <QProcess>
#include <QStorageInfo>
#include <QVariantMap>
#include <QVariant>
#include <QMap>
#include <utility>
bool DaemonManager::start(const QStringList &flags, int port)
{
if (!QFileInfo(m_monerod).isFile())
{
emit daemonStartFailure("\"" + QDir::toNativeSeparators(m_monerod) + "\" " + tr("executable is missing"));
return false;
}
m_port = port;
// prepare command line arguments and pass to monerod
QStringList arguments = flags;
arguments << "--check-updates" << "disabled";
qDebug() << "starting monerod " + m_monerod;
qDebug() << "With command line arguments " << arguments;
QMutexLocker locker(&m_daemonMutex);
m_daemon.reset(new QProcess());
// Connect output slots
connect(m_daemon.get(), &QProcess::readyReadStandardOutput, this, &DaemonManager::printOutput);
connect(m_daemon.get(), &QProcess::readyReadStandardError, this, &DaemonManager::printError);
// add state changed listener
connect(m_daemon.get(), &QProcess::stateChanged, this, &DaemonManager::stateChanged);
// Start monerod
m_daemon->start(m_monerod, arguments);
// Start start watcher
m_scheduler.run([this] {
if (startWatcher()) {
emit daemonStarted();
}
});
return true;
}
void DaemonManager::stop()
{
m_daemon->terminate();
m_scheduler.run([this]{
stopWatcher();
});
}
bool DaemonManager::isRunning() const {
return m_running;
}
bool DaemonManager::startWatcher()
{
// Check if daemon is started every 2 seconds
QElapsedTimer timer;
timer.start();
while(!m_app_exit && m_running) {
QThread::sleep(2);
if(!started()) {
qDebug() << "daemon not running. checking again in 2 seconds.";
} else {
qDebug() << "daemon is started. Waiting 5 seconds to let daemon catch up";
return true;
}
}
return false;
}
bool DaemonManager::stopWatcher()
{
// Check if daemon is running every 2 seconds. Kill if still running after 10 seconds
int counter = 0;
while(!m_app_exit && !m_running) {
QThread::sleep(2);
counter++;
if(!started()) {
return true;
} else {
qDebug() << "Daemon still running. " << counter;
if(counter >= 5) {
m_daemon->kill();
}
}
}
return false;
}
void DaemonManager::stateChanged(QProcess::ProcessState state)
{
qDebug() << "STATE CHANGED: " << state;
m_running = (state != QProcess::NotRunning);
if (state == QProcess::NotRunning) {
emit daemonStopped();
}
}
void DaemonManager::printOutput()
{
QByteArray byteArray = [this]() {
QMutexLocker locker(&m_daemonMutex);
return m_daemon->readAllStandardOutput();
}();
QStringList strLines = QString(byteArray).split("\n");
for (const auto& line: strLines) {
emit daemonConsoleUpdated(line);
}
}
void DaemonManager::printError()
{
QByteArray byteArray = [this]() {
QMutexLocker locker(&m_daemonMutex);
return m_daemon->readAllStandardError();
}();
QStringList strLines = QString(byteArray).split("\n");
for (const auto& line: strLines) {
emit daemonConsoleUpdated(line);
qDebug() << "Daemon ERROR: " + line;
}
}
bool DaemonManager::started()
{
QString status;
sendCommand({"sync_info"}, status);
qDebug() << status;
m_started = status.contains("Height:");
return m_started;
}
bool DaemonManager::sendCommand(const QStringList &cmd, QString &message) const
{
QProcess p;
QStringList external_cmd(cmd);
external_cmd << "--rpc-bind-port" << QString::number(m_port);
qDebug() << "sending external cmd: " << external_cmd;
p.start(m_monerod, external_cmd);
bool started = p.waitForFinished(-1);
message = p.readAllStandardOutput();
emit daemonConsoleUpdated(message);
return started;
}
void DaemonManager::sendCommandAsync(const QStringList &cmd, std::function<void(QVariantList)> callback) const
{
m_scheduler.run([this, cmd] {
QString message;
return QVariantList({sendCommand(cmd, message), message});
}, std::move(callback));
}
void DaemonManager::exit()
{
qDebug("DaemonManager: exit()");
m_app_exit = true;
}
//
//QVariantMap DaemonManager::validateDataDir(const QString &dataDir) const
//{
// QVariantMap result;
// bool valid = true;
// bool readOnly = false;
// int storageAvailable = 0;
// bool lmdbExists = true;
//
// QStorageInfo storage(dataDir);
// if (storage.isValid() && storage.isReady()) {
// if (storage.isReadOnly()) {
// readOnly = true;
// valid = false;
// }
//
// // Make sure there is 75GB storage available
// storageAvailable = storage.bytesAvailable()/1000/1000/1000;
// if (storageAvailable < 75) {
// valid = false;
// }
// } else {
// valid = false;
// }
//
//
// if (!QDir(dataDir+"/lmdb").exists()) {
// lmdbExists = false;
// valid = false;
// }
//
// result.insert("valid", valid);
// result.insert("lmdbExists", lmdbExists);
// result.insert("readOnly", readOnly);
// result.insert("storageAvailable", storageAvailable);
//
// return result;
//}
void DaemonManager::setMonerodPath(const QString &path) {
m_monerod = path;
}
DaemonManager::DaemonManager(QObject *parent)
: QObject(parent)
, m_scheduler(this)
{
}
DaemonManager::~DaemonManager()
{
m_scheduler.shutdownWaitForFinished();
}

@ -0,0 +1,64 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2014-2021, The Monero Project.
#ifndef DAEMONMANAGER_H
#define DAEMONMANAGER_H
#include <memory>
#include <QMutex>
#include <QObject>
#include <QUrl>
#include <QProcess>
#include <QVariantMap>
#include "FutureScheduler.h"
class DaemonManager : public QObject
{
Q_OBJECT
public:
explicit DaemonManager(QObject *parent = nullptr);
~DaemonManager() override;
bool start(const QStringList &flags, int port);
void stop();
void sendCommandAsync(const QStringList &cmd, std::function<void(QVariantList)> callback) const;
void exit();
// QVariantMap validateDataDir(const QString &dataDir) const;
void setMonerodPath(const QString &path);
bool isRunning() const;
private:
bool started();
bool sendCommand(const QStringList &cmd, QString &message) const;
bool startWatcher();
bool stopWatcher();
signals:
void daemonStarted() const;
void daemonStopped() const;
void daemonStartFailure(const QString &error) const;
void daemonConsoleUpdated(QString message) const;
public slots: