chore(cpp): apply clang-format

This commit is contained in:
Patryk Osmaczko 2022-10-19 15:41:53 +02:00 committed by osmaczko
parent 1dcc3a1f2f
commit c53264b124
104 changed files with 1736 additions and 1511 deletions

View File

@ -2,14 +2,13 @@
#include <QtQml/QQmlEngine>
namespace Status::Application {
ApplicationController::ApplicationController(QObject *parent)
: QObject{parent}
, m_dataProvider(std::make_unique<DataProvider>())
namespace Status::Application
{
}
ApplicationController::ApplicationController(QObject* parent)
: QObject{parent}
, m_dataProvider(std::make_unique<DataProvider>())
{ }
void ApplicationController::initOnLogin()
{
@ -17,23 +16,22 @@ void ApplicationController::initOnLogin()
m_dbSettings = std::make_shared<DbSettingsObj>(dbSettings);
}
QObject *ApplicationController::dbSettings() const
QObject* ApplicationController::dbSettings() const
{
return m_dbSettings.get();
}
QObject *ApplicationController::statusAccount() const
QObject* ApplicationController::statusAccount() const
{
QQmlEngine::setObjectOwnership(m_statusAccount, QQmlEngine::CppOwnership);
return m_statusAccount;
}
void ApplicationController::setStatusAccount(QObject *newStatusAccount)
void ApplicationController::setStatusAccount(QObject* newStatusAccount)
{
if (m_statusAccount == newStatusAccount)
return;
if(m_statusAccount == newStatusAccount) return;
m_statusAccount = newStatusAccount;
emit statusAccountChanged();
}
}
} // namespace Status::Application

View File

@ -1,7 +1,7 @@
#pragma once
#include "DbSettingsObj.h"
#include "DataProvider.h"
#include "DbSettingsObj.h"
#include <QObject>
#include <QtQml/qqmlregistration.h>
@ -9,7 +9,8 @@
// TODO: investigate. This line breaks qobject_cast in OnboardingController::login
//#include <Onboarding/UserAccount.h>
namespace Status::Application {
namespace Status::Application
{
/**
* @brief Responsible for providing general information and utility components
@ -23,12 +24,12 @@ class ApplicationController : public QObject
Q_PROPERTY(QObject* dbSettings READ dbSettings CONSTANT)
public:
explicit ApplicationController(QObject *parent = nullptr);
explicit ApplicationController(QObject* parent = nullptr);
Q_INVOKABLE void initOnLogin();
QObject *statusAccount() const;
void setStatusAccount(QObject *newStatusAccount);
QObject* statusAccount() const;
void setStatusAccount(QObject* newStatusAccount);
QObject* dbSettings() const;
@ -41,4 +42,4 @@ private:
std::shared_ptr<DbSettingsObj> m_dbSettings;
};
}
} // namespace Status::Application

View File

@ -6,18 +6,20 @@ namespace StatusGo = Status::StatusGo;
DataProvider::DataProvider()
: QObject(nullptr)
{
}
{ }
StatusGo::Settings::SettingsDto DataProvider::getSettings() const
{
try {
try
{
return StatusGo::Settings::getSettings();
}
catch (std::exception& e) {
catch(std::exception& e)
{
qWarning() << "DataProvider::getSettings, error: " << e.what();
}
catch (...) {
catch(...)
{
qWarning() << "DataProvider::getSettings, unknown error";
}
return StatusGo::Settings::SettingsDto{};

View File

@ -1,17 +1,18 @@
#pragma once
#include <StatusGo/SettingsAPI>
#include <QtCore/QtCore>
#include <StatusGo/SettingsAPI>
namespace Status::Application {
namespace Status::Application
{
class DataProvider: public QObject
{
Q_OBJECT
class DataProvider : public QObject
{
Q_OBJECT
public:
DataProvider();
public:
DataProvider();
StatusGo::Settings::SettingsDto getSettings() const;
};
}
StatusGo::Settings::SettingsDto getSettings() const;
};
} // namespace Status::Application

View File

@ -5,8 +5,7 @@ using namespace Status::Application;
DbSettingsObj::DbSettingsObj(StatusGo::Settings::SettingsDto rawData)
: QObject(nullptr)
, m_data(std::move(rawData))
{
}
{ }
QString DbSettingsObj::address() const
{
@ -15,8 +14,7 @@ QString DbSettingsObj::address() const
void DbSettingsObj::setAddress(const QString& value)
{
if (m_data.address == value)
return;
if(m_data.address == value) return;
m_data.address = value;
emit addressChanged();
}
@ -28,8 +26,7 @@ QString DbSettingsObj::displayName() const
void DbSettingsObj::setDisplayName(const QString& value)
{
if (m_data.displayName == value)
return;
if(m_data.displayName == value) return;
m_data.displayName = value;
emit displayNameChanged();
}
@ -41,8 +38,7 @@ QString DbSettingsObj::preferredName() const
void DbSettingsObj::setPreferredName(const QString& value)
{
if (m_data.preferredName == value)
return;
if(m_data.preferredName == value) return;
m_data.preferredName = value;
emit preferredNameChanged();
}
@ -54,8 +50,7 @@ QString DbSettingsObj::keyUid() const
void DbSettingsObj::setKeyUid(const QString& value)
{
if (m_data.keyUid == value)
return;
if(m_data.keyUid == value) return;
m_data.keyUid = value;
emit keyUidChanged();
}
@ -67,8 +62,7 @@ QString DbSettingsObj::publicKey() const
void DbSettingsObj::setPublicKey(const QString& value)
{
if (m_data.publicKey == value)
return;
if(m_data.publicKey == value) return;
m_data.publicKey = value;
emit publicKeyChanged();
}

View File

@ -4,46 +4,45 @@
#include <QtCore/QtCore>
namespace Status::Application {
namespace Status::Application
{
class DbSettingsObj: public QObject
{
Q_OBJECT
class DbSettingsObj : public QObject
{
Q_OBJECT
Q_PROPERTY(QString address READ address NOTIFY addressChanged)
Q_PROPERTY(QString displayName READ displayName NOTIFY displayNameChanged)
Q_PROPERTY(QString preferredName READ preferredName NOTIFY preferredNameChanged)
Q_PROPERTY(QString keyUid READ keyUid NOTIFY keyUidChanged)
Q_PROPERTY(QString publicKey READ publicKey NOTIFY publicKeyChanged)
Q_PROPERTY(QString address READ address NOTIFY addressChanged)
Q_PROPERTY(QString displayName READ displayName NOTIFY displayNameChanged)
Q_PROPERTY(QString preferredName READ preferredName NOTIFY preferredNameChanged)
Q_PROPERTY(QString keyUid READ keyUid NOTIFY keyUidChanged)
Q_PROPERTY(QString publicKey READ publicKey NOTIFY publicKeyChanged)
public:
explicit DbSettingsObj(StatusGo::Settings::SettingsDto rawData);
public:
explicit DbSettingsObj(StatusGo::Settings::SettingsDto rawData);
[[nodiscard]] QString address() const;
void setAddress(const QString& address);
[[nodiscard]] QString address() const;
void setAddress(const QString& address);
[[nodiscard]] QString displayName() const;
void setDisplayName(const QString& value);
[[nodiscard]] QString displayName() const;
void setDisplayName(const QString& value);
[[nodiscard]] QString preferredName() const;
void setPreferredName(const QString& value);
[[nodiscard]] QString preferredName() const;
void setPreferredName(const QString& value);
[[nodiscard]] QString keyUid() const;
void setKeyUid(const QString& value);
[[nodiscard]] QString keyUid() const;
void setKeyUid(const QString& value);
[[nodiscard]] QString publicKey() const;
void setPublicKey(const QString& value);
[[nodiscard]] QString publicKey() const;
void setPublicKey(const QString& value);
signals:
void addressChanged();
void displayNameChanged();
void preferredNameChanged();
void keyUidChanged();
void publicKeyChanged();
signals:
void addressChanged();
void displayNameChanged();
void preferredNameChanged();
void keyUidChanged();
void publicKeyChanged();
private:
StatusGo::Settings::SettingsDto m_data;
};
}
private:
StatusGo::Settings::SettingsDto m_data;
};
} // namespace Status::Application

View File

@ -7,26 +7,28 @@
#include <Helpers/helpers.h>
#include <Helpers/logs.h>
#include <QDir>
#include <QDebug>
#include <QDir>
using namespace Status;
void setApplicationInformation(QGuiApplication& app);
int main(int argc, char *argv[])
int main(int argc, char* argv[])
{
//qInstallMessageHandler(Helpers::logFormatter);
QGuiApplication app(argc, argv);
setApplicationInformation(app);
QTranslator translator;
const QStringList uiLanguages = QLocale::system().uiLanguages();
for (const QString &locale : uiLanguages) {
for(const QString& locale : uiLanguages)
{
const QString baseName = BUILD_PROJECT_NAME + QLocale(locale).name();
if (translator.load(":/i18n/" + baseName)) {
if(translator.load(":/i18n/" + baseName))
{
app.installTranslator(&translator);
break;
}
@ -35,27 +37,31 @@ int main(int argc, char *argv[])
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/Status/Application/qml/main.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreated,
&app,
[url](QObject* obj, const QUrl& objUrl) {
if(!obj && url == objUrl) QCoreApplication::exit(-1);
},
Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
void setApplicationInformation(QGuiApplication& app) {
void setApplicationInformation(QGuiApplication& app)
{
#if !defined BUILD_PROJECT_ORGANIZATION_NAME
static_assert(false, "Compile-time define missing: BUILD_PROJECT_ORGANIZATION_NAME")
#endif
app.setOrganizationName(BUILD_PROJECT_ORGANIZATION_NAME);
app.setOrganizationName(BUILD_PROJECT_ORGANIZATION_NAME);
#if !defined BUILD_PROJECT_ORGANIZATION_DOMAIN
static_assert(false, "Compile-time define missing: BUILD_PROJECT_ORGANIZATION_DOMAIN")
#endif
app.setOrganizationDomain(BUILD_PROJECT_ORGANIZATION_DOMAIN);
app.setOrganizationDomain(BUILD_PROJECT_ORGANIZATION_DOMAIN);
#if !defined BUILD_PROJECT_APPLICATION_NAME
static_assert(false, "Compile-time define missing: BUILD_PROJECT_APPLICATION_NAME")
#endif
app.setApplicationName(BUILD_PROJECT_APPLICATION_NAME);
app.setApplicationName(BUILD_PROJECT_APPLICATION_NAME);
}

View File

@ -8,14 +8,16 @@
namespace fs = std::filesystem;
namespace Status::ApplicationCore {
namespace Status::ApplicationCore
{
namespace {
constexpr auto statusFolder = "Status";
constexpr auto dataSubfolder = "data";
}
namespace
{
constexpr auto statusFolder = "Status";
constexpr auto dataSubfolder = "data";
} // namespace
UserConfiguration::UserConfiguration(QObject *parent)
UserConfiguration::UserConfiguration(QObject* parent)
: QObject{parent}
{
generateReleaseConfiguration();
@ -26,16 +28,15 @@ const QString UserConfiguration::qmlUserDataFolder() const
return toQString(m_userDataFolder.string());
}
const fs::path &UserConfiguration::userDataFolder() const
const fs::path& UserConfiguration::userDataFolder() const
{
return m_userDataFolder;
}
void UserConfiguration::setUserDataFolder(const QString &newUserDataFolder)
void UserConfiguration::setUserDataFolder(const QString& newUserDataFolder)
{
auto newVal = Status::toPath(newUserDataFolder);
if (m_userDataFolder.compare(newVal) == 0)
return;
if(m_userDataFolder.compare(newVal) == 0) return;
m_userDataFolder = newVal;
emit userDataFolderChanged();
}
@ -43,7 +44,8 @@ void UserConfiguration::setUserDataFolder(const QString &newUserDataFolder)
void UserConfiguration::generateReleaseConfiguration()
{
if(!parseFromCommandLineAndReturnTrueIfSet())
m_userDataFolder = toPath(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation))/statusFolder/dataSubfolder;
m_userDataFolder =
toPath(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation)) / statusFolder / dataSubfolder;
emit userDataFolderChanged();
}
@ -55,7 +57,8 @@ bool UserConfiguration::parseFromCommandLineAndReturnTrueIfSet()
parser.addPositionalArgument("dataDir", "Data folder");
parser.process(*QCoreApplication::instance());
auto args = parser.positionalArguments();
if (args.size() > 0) {
if(args.size() > 0)
{
m_userDataFolder = toPath(args[0]);
emit userDataFolderChanged();
return true;
@ -63,4 +66,4 @@ bool UserConfiguration::parseFromCommandLineAndReturnTrueIfSet()
return false;
}
}
} // namespace Status::ApplicationCore

View File

@ -5,12 +5,13 @@
#include <filesystem>
namespace Status::ApplicationCore {
namespace Status::ApplicationCore
{
namespace fs = std::filesystem;
/// Contains necessary data for each created account hence this will be the same path for all accounts
class UserConfiguration: public QObject
class UserConfiguration : public QObject
{
Q_OBJECT
QML_ELEMENT
@ -19,11 +20,11 @@ class UserConfiguration: public QObject
Q_PROPERTY(QString userDataFolder READ qmlUserDataFolder WRITE setUserDataFolder NOTIFY userDataFolderChanged)
public:
explicit UserConfiguration(QObject *parent = nullptr);
explicit UserConfiguration(QObject* parent = nullptr);
const QString qmlUserDataFolder() const;
const fs::path &userDataFolder() const;
void setUserDataFolder(const QString &newUserDataFolder);
const fs::path& userDataFolder() const;
void setUserDataFolder(const QString& newUserDataFolder);
signals:
void userDataFolderChanged();
@ -35,4 +36,4 @@ private:
fs::path m_userDataFolder;
};
}
} // namespace Status::ApplicationCore

View File

@ -1,17 +1,18 @@
#pragma once
#include <StatusGo/ChatAPI>
#include <QtCore/QtCore>
#include <StatusGo/ChatAPI>
namespace Status::ChatSection {
namespace Status::ChatSection
{
class ChatDataProvider: public QObject
{
Q_OBJECT
class ChatDataProvider : public QObject
{
Q_OBJECT
public:
ChatDataProvider();
public:
ChatDataProvider();
StatusGo::Chats::ChannelGroupDto getSectionData(const QString& sectionId) const;
};
}
StatusGo::Chats::ChannelGroupDto getSectionData(const QString& sectionId) const;
};
} // namespace Status::ChatSection

View File

@ -4,49 +4,50 @@
#include <QtCore/QtCore>
namespace Status::ChatSection {
namespace Status::ChatSection
{
class ChatItem: public QObject
{
Q_OBJECT
class ChatItem : public QObject
{
Q_OBJECT
Q_PROPERTY(QString id READ id CONSTANT)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
Q_PROPERTY(QColor color READ color NOTIFY colorChanged)
Q_PROPERTY(bool muted READ muted NOTIFY mutedChanged)
Q_PROPERTY(bool active READ active NOTIFY activeChanged)
Q_PROPERTY(QString id READ id CONSTANT)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
Q_PROPERTY(QColor color READ color NOTIFY colorChanged)
Q_PROPERTY(bool muted READ muted NOTIFY mutedChanged)
Q_PROPERTY(bool active READ active NOTIFY activeChanged)
public:
explicit ChatItem(StatusGo::Chats::ChatDto rawData);
public:
explicit ChatItem(StatusGo::Chats::ChatDto rawData);
[[nodiscard]] QString id() const;
[[nodiscard]] QString id() const;
[[nodiscard]] QString name() const;
void setName(const QString& value);
[[nodiscard]] QString name() const;
void setName(const QString& value);
[[nodiscard]] QString description() const;
void setDescription(const QString& value);
[[nodiscard]] QString description() const;
void setDescription(const QString& value);
[[nodiscard]] QColor color() const;
void setColor(const QColor& value);
[[nodiscard]] QColor color() const;
void setColor(const QColor& value);
[[nodiscard]] bool muted() const;
void setMuted(bool value);
[[nodiscard]] bool muted() const;
void setMuted(bool value);
[[nodiscard]] bool active() const;
void setActive(bool value);
[[nodiscard]] bool active() const;
void setActive(bool value);
signals:
void nameChanged();
void descriptionChanged();
void colorChanged();
void mutedChanged();
void activeChanged();
signals:
void nameChanged();
void descriptionChanged();
void colorChanged();
void mutedChanged();
void activeChanged();
private:
StatusGo::Chats::ChatDto m_data;
};
private:
StatusGo::Chats::ChatDto m_data;
};
using ChatItemPtr = std::shared_ptr<ChatItem>;
}
using ChatItemPtr = std::shared_ptr<ChatItem>;
} // namespace Status::ChatSection

View File

@ -1,37 +1,38 @@
#pragma once
#include "ChatItem.h"
#include "ChatDataProvider.h"
#include "ChatItem.h"
#include <Helpers/QObjectVectorModel.h>
namespace Status::ChatSection {
namespace Status::ChatSection
{
class ChatSectionController: public QObject
{
Q_OBJECT
QML_ELEMENT
class ChatSectionController : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QAbstractListModel* chatsModel READ chatsModel NOTIFY chatsModelChanged)
Q_PROPERTY(ChatItem* currentChat READ currentChat NOTIFY currentChatChanged)
Q_PROPERTY(QAbstractListModel* chatsModel READ chatsModel NOTIFY chatsModelChanged)
Q_PROPERTY(ChatItem* currentChat READ currentChat NOTIFY currentChatChanged)
public:
ChatSectionController();
public:
ChatSectionController();
QAbstractListModel* chatsModel() const;
ChatItem* currentChat() const;
QAbstractListModel* chatsModel() const;
ChatItem* currentChat() const;
Q_INVOKABLE void init(const QString& sectionId);
Q_INVOKABLE void setCurrentChatIndex(int index);
Q_INVOKABLE void init(const QString& sectionId);
Q_INVOKABLE void setCurrentChatIndex(int index);
signals:
void chatsModelChanged();
void currentChatChanged();
signals:
void chatsModelChanged();
void currentChatChanged();
private:
using ChatsModel = Helpers::QObjectVectorModel<ChatItem>;
std::shared_ptr<ChatsModel> m_chats;
std::unique_ptr<ChatDataProvider> m_dataProvider;
ChatItemPtr m_currentChat;
};
}
private:
using ChatsModel = Helpers::QObjectVectorModel<ChatItem>;
std::shared_ptr<ChatsModel> m_chats;
std::unique_ptr<ChatDataProvider> m_dataProvider;
ChatItemPtr m_currentChat;
};
} // namespace Status::ChatSection

View File

@ -6,22 +6,24 @@ namespace StatusGo = Status::StatusGo;
ChatDataProvider::ChatDataProvider()
: QObject(nullptr)
{
}
{ }
StatusGo::Chats::ChannelGroupDto ChatDataProvider::getSectionData(const QString& sectionId) const
{
try {
try
{
auto result = StatusGo::Chats::getChats();
for(auto chGroup : result.allChannelGroups) {
if (chGroup.id == sectionId)
return chGroup;
for(auto chGroup : result.allChannelGroups)
{
if(chGroup.id == sectionId) return chGroup;
}
}
catch (std::exception& e) {
catch(std::exception& e)
{
qWarning() << "ChatDataProvider::getSectionData, error: " << e.what();
}
catch (...) {
catch(...)
{
qWarning() << "ChatDataProvider::getSectionData, unknown error";
}
return StatusGo::Chats::ChannelGroupDto{};

View File

@ -5,8 +5,7 @@ using namespace Status::ChatSection;
ChatItem::ChatItem(StatusGo::Chats::ChatDto rawData)
: QObject(nullptr)
, m_data(std::move(rawData))
{
}
{ }
QString ChatItem::id() const
{
@ -20,8 +19,7 @@ QString ChatItem::name() const
void ChatItem::setName(const QString& value)
{
if (m_data.name == value)
return;
if(m_data.name == value) return;
m_data.name = value;
emit nameChanged();
}
@ -33,8 +31,7 @@ QString ChatItem::description() const
void ChatItem::setDescription(const QString& value)
{
if (m_data.description == value)
return;
if(m_data.description == value) return;
m_data.description = value;
emit descriptionChanged();
}
@ -46,8 +43,7 @@ QColor ChatItem::color() const
void ChatItem::setColor(const QColor& value)
{
if (m_data.color == value)
return;
if(m_data.color == value) return;
m_data.color = value;
emit colorChanged();
}
@ -59,8 +55,7 @@ bool ChatItem::muted() const
void ChatItem::setMuted(bool value)
{
if (m_data.muted == value)
return;
if(m_data.muted == value) return;
m_data.muted = value;
emit mutedChanged();
}
@ -72,8 +67,7 @@ bool ChatItem::active() const
void ChatItem::setActive(bool value)
{
if (m_data.active == value)
return;
if(m_data.active == value) return;
m_data.active = value;
emit activeChanged();
}

View File

@ -5,15 +5,15 @@ using namespace Status::ChatSection;
ChatSectionController::ChatSectionController()
: QObject(nullptr)
, m_dataProvider(std::make_unique<ChatDataProvider>())
{
}
{ }
void ChatSectionController::init(const QString& sectionId)
{
auto chatSectionData = m_dataProvider->getSectionData(sectionId);
assert(chatSectionData.chats.size() > 0);
std::vector<ChatItemPtr> model;
for (auto c : chatSectionData.chats) {
for(auto c : chatSectionData.chats)
{
model.push_back(std::make_shared<ChatItem>(std::move(c)));
}
m_chats = std::make_shared<ChatsModel>(std::move(model), "chat");
@ -34,8 +34,7 @@ ChatItem* ChatSectionController::currentChat() const
void ChatSectionController::setCurrentChatIndex(int index)
{
auto chat = index >= 0 && index < m_chats->size() ? m_chats->get(index) : ChatItemPtr();
if (m_currentChat == chat)
return;
if(m_currentChat == chat) return;
m_currentChat = chat;
emit currentChatChanged();

View File

@ -2,17 +2,16 @@
#include "Macros.h"
#define STATUS_READ_NLOHMAN_JSON_PROPERTY_3_ARGS(FIELD, NAME, REQUIRED) \
if(REQUIRED) \
j.at(NAME).get_to(d.FIELD); \
else if(j.contains(NAME)) \
j.at(NAME).get_to(d.FIELD); \
#define STATUS_READ_NLOHMAN_JSON_PROPERTY_3_ARGS(FIELD, NAME, REQUIRED) \
if(REQUIRED) \
j.at(NAME).get_to(d.FIELD); \
else if(j.contains(NAME)) \
j.at(NAME).get_to(d.FIELD);
#define STATUS_READ_NLOHMAN_JSON_PROPERTY_2_ARGS(FIELD, NAME) \
STATUS_READ_NLOHMAN_JSON_PROPERTY_3_ARGS(FIELD, NAME, true)
#define STATUS_READ_NLOHMAN_JSON_PROPERTY_2_ARGS(FIELD, NAME) \
STATUS_READ_NLOHMAN_JSON_PROPERTY_3_ARGS(FIELD, NAME, true)
#define STATUS_READ_NLOHMAN_JSON_PROPERTY_1_ARGS(FIELD) \
STATUS_READ_NLOHMAN_JSON_PROPERTY_2_ARGS(FIELD, #FIELD)
#define STATUS_READ_NLOHMAN_JSON_PROPERTY_1_ARGS(FIELD) STATUS_READ_NLOHMAN_JSON_PROPERTY_2_ARGS(FIELD, #FIELD)
// This macro reads prop from the nlohman json object. It implies that nlohman json object is named `j` and the struct
// instance that json object should be mapped to is named `d`.
@ -27,12 +26,8 @@ STATUS_READ_NLOHMAN_JSON_PROPERTY_2_ARGS(FIELD, #FIELD)
// STATUS_READ_NLOHMAN_JSON_PROPERTY(field, "realFieldName")
// STATUS_READ_NLOHMAN_JSON_PROPERTY(field, "realFieldName", false)
//
#define STATUS_READ_NLOHMAN_JSON_PROPERTY(...) \
STATUS_EXPAND( \
STATUS_MACRO_SELECTOR_3_ARGS( \
__VA_ARGS__, \
STATUS_READ_NLOHMAN_JSON_PROPERTY_3_ARGS, \
STATUS_READ_NLOHMAN_JSON_PROPERTY_2_ARGS, \
STATUS_READ_NLOHMAN_JSON_PROPERTY_1_ARGS \
)(__VA_ARGS__) \
)
#define STATUS_READ_NLOHMAN_JSON_PROPERTY(...) \
STATUS_EXPAND(STATUS_MACRO_SELECTOR_3_ARGS(__VA_ARGS__, \
STATUS_READ_NLOHMAN_JSON_PROPERTY_3_ARGS, \
STATUS_READ_NLOHMAN_JSON_PROPERTY_2_ARGS, \
STATUS_READ_NLOHMAN_JSON_PROPERTY_1_ARGS)(__VA_ARGS__))

View File

@ -4,9 +4,7 @@
#define STATUS_EXPAND(x) x
// 2 arguments macro selector.
#define STATUS_MACRO_SELECTOR_2_ARGS(_1, _2, selected, ...) \
selected
#define STATUS_MACRO_SELECTOR_2_ARGS(_1, _2, selected, ...) selected
// 3 arguments macro selector.
#define STATUS_MACRO_SELECTOR_3_ARGS(_1, _2, _3, selected, ...) \
selected
#define STATUS_MACRO_SELECTOR_3_ARGS(_1, _2, _3, selected, ...) selected

View File

@ -7,9 +7,10 @@
using json = nlohmann::json;
namespace Status::Helpers {
namespace Status::Helpers
{
template<typename T>
template <typename T>
using IsNotReference = typename std::enable_if<!std::is_reference<T>::value, void>::type;
/// Featureless version of https://github.com/joboccara/NamedType that works with nlohmann::json
@ -19,27 +20,52 @@ class NamedType
public:
using UnderlyingType = T;
explicit constexpr NamedType(T const& value) : m_value(value) {}
template<typename T_ = T, typename = IsNotReference<T_>>
explicit constexpr NamedType(T&& value) : m_value(std::move(value)) {}
explicit constexpr NamedType(T const& value)
: m_value(value)
{ }
template <typename T_ = T, typename = IsNotReference<T_>>
explicit constexpr NamedType(T&& value)
: m_value(std::move(value))
{ }
explicit constexpr NamedType() = default;
constexpr T& get() {
constexpr T& get()
{
return m_value;
}
constexpr std::remove_reference_t<T> const& get() const {
constexpr std::remove_reference_t<T> const& get() const
{
return m_value;
}
bool operator<(const NamedType<T, Parameter> &other) const { return m_value < other.m_value; };
bool operator>(const NamedType<T, Parameter> &other) const { return m_value > other.m_value; };
bool operator<=(const NamedType<T, Parameter> &other) const { return m_value <= other.m_value; };
bool operator>=(const NamedType<T, Parameter> &other) const { return m_value >= other.m_value; };
bool operator==(const NamedType<T, Parameter> &other) const { return m_value == other.m_value; };
bool operator!=(const NamedType<T, Parameter> &other) const { return m_value != other.m_value; };
bool operator<(const NamedType<T, Parameter>& other) const
{
return m_value < other.m_value;
};
bool operator>(const NamedType<T, Parameter>& other) const
{
return m_value > other.m_value;
};
bool operator<=(const NamedType<T, Parameter>& other) const
{
return m_value <= other.m_value;
};
bool operator>=(const NamedType<T, Parameter>& other) const
{
return m_value >= other.m_value;
};
bool operator==(const NamedType<T, Parameter>& other) const
{
return m_value == other.m_value;
};
bool operator!=(const NamedType<T, Parameter>& other) const
{
return m_value != other.m_value;
};
T &operator=(NamedType<T, Parameter> const& rhs) {
T& operator=(NamedType<T, Parameter> const& rhs)
{
return m_value = rhs.m_value;
};
@ -48,16 +74,18 @@ private:
};
template <typename T, typename P>
void to_json(json& j, const NamedType<T, P>& p) {
void to_json(json& j, const NamedType<T, P>& p)
{
j = p.get();
}
template <typename T, typename P>
void from_json(const json& j, NamedType<T, P>& p) {
void from_json(const json& j, NamedType<T, P>& p)
{
p = NamedType<T, P>{j.get<T>()};
}
}
} // namespace Status::Helpers
namespace std
{

View File

@ -2,96 +2,110 @@
#include <QAbstractListModel>
#include <QQmlEngine>
namespace Status::Helpers {
namespace Status::Helpers
{
/// Generic typed QObject provider model
///
/// Supports: source model update
/// \todo rename it to SharedQObjectVectorModel
/// \todo consider "separating class template interface and implementation: move impl to .hpp file and include it at the end of .h file. That's not affect compilation time, but it better to read" propsed by @MishkaRogachev
template<typename T>
template <typename T>
class QObjectVectorModel final : public QAbstractListModel
{
static_assert(std::is_base_of<QObject, T>::value, "Template parameter (T) not a QObject");
public:
using ObjectContainer = std::vector<std::shared_ptr<T>>;
explicit QObjectVectorModel(ObjectContainer initialObjects, const char* objectRoleName, QObject* parent = nullptr)
: QAbstractListModel(parent)
, m_objects(std::move(initialObjects))
, m_roleName(objectRoleName)
{
}
{ }
explicit QObjectVectorModel(const char* objectRoleName, QObject* parent = nullptr)
: QObjectVectorModel(ObjectContainer{}, objectRoleName, parent)
{}
~QObjectVectorModel() {};
{ }
~QObjectVectorModel(){};
QHash<int, QByteArray> roleNames() const override {
QHash<int, QByteArray> roleNames() const override
{
return {{ObjectRole, m_roleName}};
};
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override {
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
Q_UNUSED(parent)
return m_objects.size();
}
virtual QVariant data(const QModelIndex& index, int role) const override {
if(!QAbstractItemModel::checkIndex(index) || role != ObjectRole)
return QVariant();
virtual QVariant data(const QModelIndex& index, int role) const override
{
if(!QAbstractItemModel::checkIndex(index) || role != ObjectRole) return QVariant();
return QVariant::fromValue<QObject*>(m_objects[index.row()].get());
}
const T* at(size_t pos) const {
const T* at(size_t pos) const
{
return m_objects.at(pos).get();
};
std::shared_ptr<T> get(size_t pos) {
std::shared_ptr<T> get(size_t pos)
{
return m_objects.at(pos);
};
size_t size() const {
size_t size() const
{
return m_objects.size();
};
void reset(const ObjectContainer& objects) {
void reset(const ObjectContainer& objects)
{
beginResetModel();
m_objects = objects;
endResetModel();
};
void clear() {
void clear()
{
reset({});
};
void push_back(const std::shared_ptr<T>& newValue) {
void push_back(const std::shared_ptr<T>& newValue)
{
beginInsertRows(QModelIndex(), m_objects.size(), m_objects.size());
m_objects.push_back(newValue);
endInsertRows();
};
void resize(size_t count) {
if(count > m_objects.size()) {
void resize(size_t count)
{
if(count > m_objects.size())
{
beginInsertRows(QModelIndex(), m_objects.size(), count - 1);
m_objects.resize(count);
endInsertRows();
}
else if(count < m_objects.size()) {
else if(count < m_objects.size())
{
beginRemoveRows(QModelIndex(), count, m_objects.size() - 1);
m_objects.resize(count);
endRemoveRows();
}
};
void set(size_t row, const std::shared_ptr<T>& newVal) {
void set(size_t row, const std::shared_ptr<T>& newVal)
{
m_objects.at(row) = newVal;
emit dataChanged(index(row), index(row), {});
};
const ObjectContainer &objects() const { return m_objects; };
const ObjectContainer& objects() const
{
return m_objects;
};
private:
ObjectContainer m_objects;
@ -101,4 +115,4 @@ private:
constexpr static auto ObjectRole = Qt::UserRole + 1;
};
}
} // namespace Status::Helpers

View File

@ -5,7 +5,7 @@
namespace Status::Helpers
{
template<typename T>
template <typename T>
class Singleton
{
public:
@ -18,10 +18,10 @@ public:
}
Singleton<T>(const Singleton<T>&) = delete;
Singleton<T>& operator = (const Singleton<T>&) = delete;
private:
Singleton<T>& operator=(const Singleton<T>&) = delete;
private:
Singleton<T>() = default;
};
}
} // namespace Status::Helpers

View File

@ -2,14 +2,17 @@
namespace fs = std::filesystem;
namespace Status {
namespace Status
{
QString toQString(const fs::path &path) {
QString toQString(const fs::path& path)
{
return QString::fromStdString(path.string());
}
fs::path toPath(const QString &pathStr) {
fs::path toPath(const QString& pathStr)
{
return fs::path(pathStr.toStdString());
}
}
} // namespace Status

View File

@ -2,9 +2,9 @@
#include "helpers.h"
#include <QString>
#include <QByteArray>
#include <QColor>
#include <QString>
#include <QUrl>
#include <filesystem>
@ -13,7 +13,8 @@
using json = nlohmann::json;
namespace Status {
namespace Status
{
QString toQString(const std::filesystem::path& path);
@ -21,28 +22,35 @@ std::filesystem::path toPath(const QString& pathStr);
} // namespace Status
namespace nlohmann {
namespace nlohmann
{
template<>
struct adl_serializer<QString> {
static void to_json(json& j, const QString& str) {
template <>
struct adl_serializer<QString>
{
static void to_json(json& j, const QString& str)
{
j = str.toStdString();
}
static void from_json(const json& j, QString& str) {
static void from_json(const json& j, QString& str)
{
str = QString::fromStdString(j.get<std::string>());
}
};
using namespace std::string_literals;
template<>
struct adl_serializer<QByteArray> {
static void to_json(json& j, const QByteArray& data) {
template <>
struct adl_serializer<QByteArray>
{
static void to_json(json& j, const QByteArray& data)
{
j = data.toStdString();
}
static void from_json(const json& j, QByteArray& data) {
static void from_json(const json& j, QByteArray& data)
{
auto str = j.get<std::string>();
if(str.size() >= 2 && Status::Helpers::iequals(str, "0x"s, 2))
data = QByteArray::fromHex(QByteArray::fromRawData(str.c_str() + 2 * sizeof(str[0]), str.size() - 2));
@ -51,35 +59,44 @@ struct adl_serializer<QByteArray> {
}
};
template<>
struct adl_serializer<QColor> {
static void to_json(json& j, const QColor& color) {
template <>
struct adl_serializer<QColor>
{
static void to_json(json& j, const QColor& color)
{
j = color.name();
}
static void from_json(const json& j, QColor& color) {
static void from_json(const json& j, QColor& color)
{
color = QColor(QString::fromStdString(j.get<std::string>()));
}
};
template<>
struct adl_serializer<QUrl> {
static void to_json(json& j, const QUrl& url) {
template <>
struct adl_serializer<QUrl>
{
static void to_json(json& j, const QUrl& url)
{
j = url.toString();
}
static void from_json(const json& j, QUrl& url) {
static void from_json(const json& j, QUrl& url)
{
url = QUrl(QString::fromStdString(j.get<std::string>()));
}
};
template<typename T>
struct adl_serializer<std::optional<T>> {
static void to_json(json& j, const std::optional<T>& opt) {
template <typename T>
struct adl_serializer<std::optional<T>>
{
static void to_json(json& j, const std::optional<T>& opt)
{
j = opt.value();
}
static void from_json(const json& j, std::optional<T>& opt) {
static void from_json(const json& j, std::optional<T>& opt)
{
opt.emplace(j.get<T>());
}
};

View File

@ -4,53 +4,56 @@
#include <QObject>
#include <string>
#include <map>
#include <memory.h>
#include <string>
namespace Status::Helpers {
namespace Status::Helpers
{
constexpr bool isDebugBuild()
{
#if defined BUILD_DEBUG
#if defined BUILD_DEBUG
return true;
#else
#else
return false;
#endif
#endif
}
/// Case insensitive comparision with optional limitation to first \c len characters
/// \note \c T entry type must support \c tolower
/// \todo test me
template<typename T>
template <typename T>
bool iequals(const T& a, const T& b, size_t len = -1)
{
return len < a.size() && len < b.size() &&
std::equal(a.begin(), len >= 0 ? a.end() : a.begin() + len,
b.begin(), len >= 0 ? b.end() : b.begin() + len,
[](auto a, auto b) {
return tolower(a) == tolower(b);
});
std::equal(a.begin(),
len >= 0 ? a.end() : a.begin() + len,
b.begin(),
len >= 0 ? b.end() : b.begin() + len,
[](auto a, auto b) { return tolower(a) == tolower(b); });
}
template<typename KeyType, typename ValT>
template <typename KeyType, typename ValT>
std::vector<KeyType> getKeys(const std::map<KeyType, ValT>& map)
{
std::vector<KeyType> keys;
keys.reserve(map.size());
for (const auto& [key, _] : map)
for(const auto& [key, _] : map)
keys.push_back(key);
return keys;
}
static void doDeleteLater(QObject *obj) {
static void doDeleteLater(QObject* obj)
{
obj->deleteLater();
}
// TODO: use https://en.cppreference.com/w/cpp/memory/shared_ptr/allocate_shared
template<typename T, typename ...Args>
std::shared_ptr<T> makeSharedQObject(Args&& ...args) {
template <typename T, typename... Args>
std::shared_ptr<T> makeSharedQObject(Args&&... args)
{
return std::shared_ptr<T>(new T(std::forward<Args>(args)...), doDeleteLater);
}
}
} // namespace Status::Helpers

View File

@ -8,14 +8,15 @@
#include "BuildConfiguration.h"
namespace Status::Helpers {
namespace Status::Helpers
{
void logFormatter(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
// TODO: Refactor it into development-tools app
//if(isDebugBuild()) {
std::cout << msg.toLocal8Bit().data() << std::endl;
return;
std::cout << msg.toLocal8Bit().data() << std::endl;
return;
//}
QByteArray localMsg = msg.toLocal8Bit();
@ -37,7 +38,12 @@ void logFormatter(QtMsgType type, const QMessageLogContext& context, const QStri
case QtFatalMsg: log = "\033[0;31m!!! \033[0m%s \033[1m%s \033[0;33mfile=\033[94m%s:%u %s\n"; break;
}
fprintf(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout,
log, timestamp.constData(), localMsg.constData(), file, context.line, function.constData());
log,
timestamp.constData(),
localMsg.constData(),
file,
context.line,
function.constData());
}
} // namespace
} // namespace Status::Helpers

View File

@ -4,9 +4,10 @@
#include <QDebug>
#include <QString>
namespace Status::Helpers {
namespace Status::Helpers
{
/// Formats with colloring output if not a development build
void logFormatter(QtMsgType type, const QMessageLogContext& context, const QString& msg);
}
} // namespace Status::Helpers

View File

@ -3,16 +3,14 @@
#include <StatusGo/Accounts/Accounts.h>
#include <StatusGo/Accounts/AccountsAPI.h>
#include <StatusGo/General.h>
#include <StatusGo/Utils.h>
#include <StatusGo/Messenger/Service.h>
#include <StatusGo/Utils.h>
#include <Helpers/conversions.h>
#include <optional>
std::optional<QString>
getDataFromFile(const fs::path &path)
std::optional<QString> getDataFromFile(const fs::path& path)
{
QFile jsonFile{Status::toQString(path)};
if(!jsonFile.open(QIODevice::ReadOnly))
@ -34,8 +32,7 @@ namespace Status::Onboarding
AccountsService::AccountsService()
: m_isFirstTimeAccountLogin(false)
{
}
{ }
bool AccountsService::init(const fs::path& statusgoDataDir)
{
@ -47,7 +44,7 @@ bool AccountsService::init(const fs::path& statusgoDataDir)
return false;
}
for(const auto &genAddressObj : response.result)
for(const auto& genAddressObj : response.result)
{
auto gAcc = GeneratedMultiAccount::toGeneratedMultiAccount(genAddressObj.toObject());
gAcc.alias = generateAlias(gAcc.derivedAccounts.whisper.publicKey);
@ -67,7 +64,7 @@ std::vector<MultiAccount> AccountsService::openAndListAccounts()
const auto multiAccounts = response.result;
std::vector<MultiAccount> result;
for(const auto &value : multiAccounts)
for(const auto& value : multiAccounts)
{
result.push_back(MultiAccount::toMultiAccount(value.toObject()));
}
@ -79,13 +76,14 @@ const std::vector<GeneratedMultiAccount>& AccountsService::generatedAccounts() c
return m_generatedAccounts;
}
bool AccountsService::setupAccountAndLogin(const QString &accountId, const QString &password, const QString &displayName)
bool AccountsService::setupAccountAndLogin(const QString& accountId,
const QString& password,
const QString& displayName)
{
const QString installationId(QUuid::createUuid().toString(QUuid::WithoutBraces));
const QJsonObject accountData(getAccountDataForAccountId(accountId, displayName));
if(!setKeyStoreDir(accountData.value("key-uid").toString()))
return false;
if(!setKeyStoreDir(accountData.value("key-uid").toString())) return false;
QJsonArray subAccountData(getSubaccountDataForAccountId(accountId, displayName));
QJsonObject settings(getAccountSettings(accountId, installationId, displayName));
@ -94,8 +92,7 @@ bool AccountsService::setupAccountAndLogin(const QString &accountId, const QStri
auto hashedPassword(Utils::hashPassword(password));
// This initialize the DB if first time running. Required for storing accounts
if(StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()).containsError())
return false;
if(StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()).containsError()) return false;
AccountsService::storeAccount(accountId, hashedPassword);
AccountsService::storeDerivedAccounts(accountId, hashedPassword, Constants::General::AccountDefaultPaths);
@ -120,7 +117,7 @@ bool AccountsService::isFirstTimeAccountLogin() const
return m_isFirstTimeAccountLogin;
}
bool AccountsService::setKeyStoreDir(const QString &key)
bool AccountsService::setKeyStoreDir(const QString& key)
{
m_keyStoreDir = m_statusgoDataDir / m_keyStoreDirName / key.toStdString();
const auto response = StatusGo::General::initKeystore(m_keyStoreDir.c_str());
@ -130,8 +127,7 @@ bool AccountsService::setKeyStoreDir(const QString &key)
QString AccountsService::login(MultiAccount account, const QString& password)
{
// This is a requirement. Make it more explicit into the status go module
if(!setKeyStoreDir(account.keyUid))
return QString("Failed to initialize keystore before logging in");
if(!setKeyStoreDir(account.keyUid)) return QString("Failed to initialize keystore before logging in");
// This initialize the DB if first time running. Required before logging in
if(StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()).containsError())
@ -141,8 +137,8 @@ QString AccountsService::login(MultiAccount account, const QString& password)
const QString thumbnailImage;
const QString largeImage;
const auto response = StatusGo::Accounts::login(account.name, account.keyUid, hashedPassword,
thumbnailImage, largeImage);
const auto response =
StatusGo::Accounts::login(account.name, account.keyUid, hashedPassword, thumbnailImage, largeImage);
if(response.containsError())
{
qWarning() << response.error.message;
@ -174,13 +170,14 @@ QString AccountsService::generateAlias(const QString& publicKey)
return response.result;
}
void AccountsService::deleteMultiAccount(const MultiAccount &account)
void AccountsService::deleteMultiAccount(const MultiAccount& account)
{
StatusGo::Accounts::deleteMultiaccount(account.keyUid, m_keyStoreDir);
}
DerivedAccounts AccountsService::storeDerivedAccounts(const QString& accountId, const StatusGo::HashedPassword& password,
const std::vector<Accounts::DerivationPath> &paths)
DerivedAccounts AccountsService::storeDerivedAccounts(const QString& accountId,
const StatusGo::HashedPassword& password,
const std::vector<Accounts::DerivationPath>& paths)
{
const auto response = StatusGo::Accounts::storeDerivedAccounts(accountId, password, paths);
if(response.containsError())
@ -202,11 +199,14 @@ StoredMultiAccount AccountsService::storeAccount(const QString& accountId, const
return toStoredMultiAccount(response.result);
}
MultiAccount AccountsService::saveAccountAndLogin(const StatusGo::HashedPassword& password, const QJsonObject& account,
const QJsonArray& subaccounts, const QJsonObject& settings,
const QJsonObject& config)
MultiAccount AccountsService::saveAccountAndLogin(const StatusGo::HashedPassword& password,
const QJsonObject& account,
const QJsonArray& subaccounts,
const QJsonObject& settings,
const QJsonObject& config)
{
if(!StatusGo::Accounts::saveAccountAndLogin(password, account, subaccounts, settings, config)) {
if(!StatusGo::Accounts::saveAccountAndLogin(password, account, subaccounts, settings, config))
{
qWarning() << "Failed saving acccount" << account.value("name");
return MultiAccount();
}
@ -215,17 +215,18 @@ MultiAccount AccountsService::saveAccountAndLogin(const StatusGo::HashedPassword
return MultiAccount::toMultiAccount(account);
}
QJsonObject AccountsService::prepareAccountJsonObject(const GeneratedMultiAccount& account, const QString &displayName) const
QJsonObject AccountsService::prepareAccountJsonObject(const GeneratedMultiAccount& account,
const QString& displayName) const
{
return QJsonObject{{"name", displayName.isEmpty() ? account.alias : displayName},
{"address", account.address},
{"key-uid", account.keyUid},
{"keycard-pairing", QJsonValue()}};
{"address", account.address},
{"key-uid", account.keyUid},
{"keycard-pairing", QJsonValue()}};
}
QJsonObject AccountsService::getAccountDataForAccountId(const QString &accountId, const QString &displayName) const
QJsonObject AccountsService::getAccountDataForAccountId(const QString& accountId, const QString& displayName) const
{
for(const GeneratedMultiAccount &acc : m_generatedAccounts)
for(const GeneratedMultiAccount& acc : m_generatedAccounts)
{
if(acc.id == accountId)
{
@ -245,33 +246,28 @@ QJsonObject AccountsService::getAccountDataForAccountId(const QString &accountId
return QJsonObject();
}
QJsonArray AccountsService::prepareSubaccountJsonObject(const GeneratedMultiAccount& account, const QString &displayName) const
QJsonArray AccountsService::prepareSubaccountJsonObject(const GeneratedMultiAccount& account,
const QString& displayName) const
{
return {
QJsonObject{
{"public-key", account.derivedAccounts.defaultWallet.publicKey},
{"address", account.derivedAccounts.defaultWallet.address},
{"color", "#4360df"},
{"wallet", true},
{"path", Constants::General::PathDefaultWallet.get()},
{"name", "Status account"},
{"derived-from", account.address}
},
QJsonObject{
{"public-key", account.derivedAccounts.whisper.publicKey},
{"address", account.derivedAccounts.whisper.address},
{"name", displayName.isEmpty() ? account.alias : displayName},
{"path", Constants::General::PathWhisper.get()},
{"chat", true},
{"derived-from", ""}
}
};
return {QJsonObject{{"public-key", account.derivedAccounts.defaultWallet.publicKey},
{"address", account.derivedAccounts.defaultWallet.address},
{"color", "#4360df"},
{"wallet", true},
{"path", Constants::General::PathDefaultWallet.get()},
{"name", "Status account"},
{"derived-from", account.address}},
QJsonObject{{"public-key", account.derivedAccounts.whisper.publicKey},
{"address", account.derivedAccounts.whisper.address},
{"name", displayName.isEmpty() ? account.alias : displayName},
{"path", Constants::General::PathWhisper.get()},
{"chat", true},
{"derived-from", ""}}};
}
QJsonArray AccountsService::getSubaccountDataForAccountId(const QString& accountId, const QString &displayName) const
QJsonArray AccountsService::getSubaccountDataForAccountId(const QString& accountId, const QString& displayName) const
{
// "All these for loops with a nested if cry for a std::find_if :)"
for(const GeneratedMultiAccount &acc : m_generatedAccounts)
for(const GeneratedMultiAccount& acc : m_generatedAccounts)
{
if(acc.id == accountId)
{
@ -297,42 +293,42 @@ QString AccountsService::generateSigningPhrase(int count) const
for(int i = 0; i < count; i++)
{
words.append(Constants::SigningPhrases[QRandomGenerator::global()->bounded(
static_cast<int>(Constants::SigningPhrases.size()))]);
static_cast<int>(Constants::SigningPhrases.size()))]);
}
return words.join(" ");
}
QJsonObject AccountsService::prepareAccountSettingsJsonObject(const GeneratedMultiAccount& account,
const QString& installationId,
const QString& displayName) const
const QString& installationId,
const QString& displayName) const
{
return QJsonObject{
{"key-uid", account.keyUid},
{"mnemonic", account.mnemonic},
{"public-key", account.derivedAccounts.whisper.publicKey},
{"name", account.alias},
{"display-name", displayName},
{"address", account.address},
{"eip1581-address", account.derivedAccounts.eip1581.address},
{"dapps-address", account.derivedAccounts.defaultWallet.address},
{"wallet-root-address", account.derivedAccounts.walletRoot.address},
{"preview-privacy?", true},
{"signing-phrase", generateSigningPhrase(3)},
{"log-level", "INFO"},
{"latest-derived-path", 0},
{"currency", "usd"},
{"networks/networks", QJsonArray()},
{"networks/current-network", ""},
{"wallet/visible-tokens", QJsonObject()},
{"waku-enabled", true},
{"appearance", 0},
{"installation-id", installationId}
};
return QJsonObject{{"key-uid", account.keyUid},
{"mnemonic", account.mnemonic},
{"public-key", account.derivedAccounts.whisper.publicKey},
{"name", account.alias},
{"display-name", displayName},
{"address", account.address},
{"eip1581-address", account.derivedAccounts.eip1581.address},
{"dapps-address", account.derivedAccounts.defaultWallet.address},
{"wallet-root-address", account.derivedAccounts.walletRoot.address},
{"preview-privacy?", true},
{"signing-phrase", generateSigningPhrase(3)},
{"log-level", "INFO"},
{"latest-derived-path", 0},
{"currency", "usd"},
{"networks/networks", QJsonArray()},
{"networks/current-network", ""},
{"wallet/visible-tokens", QJsonObject()},
{"waku-enabled", true},
{"appearance", 0},
{"installation-id", installationId}};
}
QJsonObject AccountsService::getAccountSettings(const QString& accountId, const QString& installationId, const QString &displayName) const
QJsonObject AccountsService::getAccountSettings(const QString& accountId,
const QString& installationId,
const QString& displayName) const
{
for(const GeneratedMultiAccount &acc : m_generatedAccounts)
for(const GeneratedMultiAccount& acc : m_generatedAccounts)
if(acc.id == accountId)
{
@ -363,7 +359,8 @@ QJsonArray getNodes(const QJsonObject& fleet, const QString& nodeType)
QJsonObject AccountsService::getDefaultNodeConfig(const QString& installationId) const
{
try {
try
{
auto templateNodeConfigJsonStr = getDataFromFile(":/Status/StaticConfig/node-config.json").value();
auto fleetJson = getDataFromFile(":/Status/StaticConfig/fleets.json").value();
auto infuraKey = getDataFromFile(":/Status/StaticConfig/infura_key").value();
@ -376,8 +373,8 @@ QJsonObject AccountsService::getDefaultNodeConfig(const QString& installationId)
auto DEFAULT_TORRENT_CONFIG_DATADIR = m_statusgoDataDir / "archivedata";
auto DEFAULT_TORRENT_CONFIG_TORRENTDIR = m_statusgoDataDir / "torrents";
auto nodeConfigJsonStr = templateNodeConfigJsonStr
.replace("%INSTALLATIONID%", installationId)
auto nodeConfigJsonStr =
templateNodeConfigJsonStr.replace("%INSTALLATIONID%", installationId)
.replace("%INFURA_TOKEN_RESOLVED%", infuraKey)
.replace("%DEFAULT_TORRENT_CONFIG_PORT%", QString::number(DEFAULT_TORRENT_CONFIG_PORT))
.replace("%DEFAULT_TORRENT_CONFIG_DATADIR%", DEFAULT_TORRENT_CONFIG_DATADIR.c_str())
@ -413,9 +410,11 @@ QJsonObject AccountsService::getDefaultNodeConfig(const QString& installationId)
nodeConfigJson["KeyStoreDir"] = toQString(fs::relative(m_keyStoreDir, m_statusgoDataDir));
return nodeConfigJson;
} catch (std::bad_optional_access) {
}
catch(std::bad_optional_access)
{
return QJsonObject();
}
}
}
} // namespace Status::Onboarding

View File

@ -44,7 +44,7 @@ public:
[[nodiscard]] bool isFirstTimeAccountLogin() const override;
/// \see ServiceInterface
bool setKeyStoreDir(const QString &key) override;
bool setKeyStoreDir(const QString& key) override;
/// \todo login@src/app_service/service/accounts/service.nim adds custom configuration for defaults
/// to account for legacy user DBs. See if this is required to replicate or add proper migration logic
@ -54,18 +54,21 @@ public:
QString generateAlias(const QString& publicKey) override;
void deleteMultiAccount(const MultiAccount &account) override;
void deleteMultiAccount(const MultiAccount& account) override;
private:
QJsonObject prepareAccountJsonObject(const GeneratedMultiAccount& account, const QString& displayName) const;
DerivedAccounts storeDerivedAccounts(const QString& accountId, const StatusGo::HashedPassword& password,
DerivedAccounts storeDerivedAccounts(const QString& accountId,
const StatusGo::HashedPassword& password,
const std::vector<Accounts::DerivationPath>& paths);
StoredMultiAccount storeAccount(const QString& accountId, const StatusGo::HashedPassword& password);
MultiAccount saveAccountAndLogin(const StatusGo::HashedPassword& password, const QJsonObject& account,
const QJsonArray& subaccounts, const QJsonObject& settings,
const QJsonObject& config);
MultiAccount saveAccountAndLogin(const StatusGo::HashedPassword& password,
const QJsonObject& account,
const QJsonArray& subaccounts,
const QJsonObject& settings,
const QJsonObject& config);
QJsonObject getAccountDataForAccountId(const QString& accountId, const QString& displayName) const;
@ -79,7 +82,8 @@ private:
const QString& installationId,
const QString& displayName) const;
QJsonObject getAccountSettings(const QString& accountId, const QString& installationId, const QString& displayName) const;
QJsonObject
getAccountSettings(const QString& accountId, const QString& installationId, const QString& displayName) const;
QJsonObject getDefaultNodeConfig(const QString& installationId) const;
@ -97,4 +101,4 @@ private:
static constexpr auto m_keyStoreDirName = "keystore";
};
}
} // namespace Status::Onboarding

View File

@ -1,7 +1,7 @@
#pragma once
#include "MultiAccount.h"
#include "GeneratedMultiAccount.h"
#include "MultiAccount.h"
#include <filesystem>
@ -13,7 +13,6 @@ namespace Status::Onboarding
class AccountsServiceInterface
{
public:
virtual ~AccountsServiceInterface() = default;
/// Generates and cache addresses accessible by \c generatedAccounts
@ -26,7 +25,8 @@ public:
[[nodiscard]] virtual const std::vector<GeneratedMultiAccount>& generatedAccounts() const = 0;
/// Configure an generated account. \a accountID must be sourced from \c generatedAccounts
virtual bool setupAccountAndLogin(const QString& accountID, const QString& password, const QString& displayName) = 0;
virtual bool
setupAccountAndLogin(const QString& accountID, const QString& password, const QString& displayName) = 0;
/// Account that is currently logged-in
[[nodiscard]] virtual const MultiAccount& getLoggedInAccount() const = 0;
@ -37,7 +37,7 @@ public:
[[nodiscard]] virtual bool isFirstTimeAccountLogin() const = 0;
/// Set and initializes the keystore directory. \see StatusGo::General::initKeystore
virtual bool setKeyStoreDir(const QString &key) = 0;
virtual bool setKeyStoreDir(const QString& key) = 0;
virtual QString login(MultiAccount account, const QString& password) = 0;
@ -45,9 +45,9 @@ public:
virtual QString generateAlias(const QString& publicKey) = 0;
virtual void deleteMultiAccount(const MultiAccount &account) = 0;
virtual void deleteMultiAccount(const MultiAccount& account) = 0;
};
using AccountsServiceInterfacePtr = std::shared_ptr<AccountsServiceInterface>;
}
} // namespace Status::Onboarding

View File

@ -1,8 +1,8 @@
#pragma once
#include "Common/Constants.h"
#include "Common/SigningPhrases.h"
#include "Common/Json.h"
#include "Common/SigningPhrases.h"
#include <QtCore>
@ -27,7 +27,7 @@ struct DerivedAccountDetails
result.publicKey = Json::getMandatoryProp(jsonObj, "publicKey")->toString();
result.address = Json::getMandatoryProp(jsonObj, "address")->toString();
}
catch (std::exception e)
catch(std::exception e)
{
qWarning() << QString("Mapping DerivedAccountDetails failed: %1").arg(e.what());
}
@ -47,7 +47,7 @@ struct DerivedAccounts
{
auto result = DerivedAccounts();
for(const auto &derivationPath : jsonObj.keys())
for(const auto& derivationPath : jsonObj.keys())
{
auto derivedObj = jsonObj.value(derivationPath).toObject();
if(derivationPath == Constants::General::PathWhisper.get())
@ -76,17 +76,19 @@ struct StoredMultiAccount
{
QString publicKey;
QString address;
};
static StoredMultiAccount toStoredMultiAccount(const QJsonObject& jsonObj)
{
auto result = StoredMultiAccount();
try {
try
{
result.address = Json::getMandatoryProp(jsonObj, "address")->toString();
result.publicKey = Json::getMandatoryProp(jsonObj, "publicKey")->toString();
} catch (std::exception e) {
}
catch(std::exception e)
{
qWarning() << QString("Mapping StoredMultiAccount failed: %1").arg(e.what());
}
@ -128,7 +130,7 @@ struct GeneratedMultiAccount
result.derivedAccounts = DerivedAccounts::toDerivedAccounts(derivedObj);
}
}
catch (std::exception e)
catch(std::exception e)
{
qWarning() << QString("Mapping GeneratedMultiAccount failed: %1").arg(e.what());
}
@ -137,4 +139,4 @@ struct GeneratedMultiAccount
}
};
}
} // namespace Status::Onboarding

View File

@ -1,8 +1,8 @@
#pragma once
#include "Common/Constants.h"
#include "Common/SigningPhrases.h"
#include "Common/Json.h"
#include "Common/SigningPhrases.h"
#include <StatusGo/Accounts/accounts_types.h>
@ -39,11 +39,11 @@ struct MultiAccount
{
result.name = Json::getMandatoryProp(jsonObj, "name")->toString();
auto timestampIt = Json::getProp(jsonObj, "timestamp");
if(timestampIt != jsonObj.constEnd()) {
if(timestampIt != jsonObj.constEnd())
{
bool ok = false;
auto t = timestampIt->toString().toLong(&ok);
if(ok)
result.timestamp = t;
if(ok) result.timestamp = t;
}
result.keycardPairing = Json::getMandatoryProp(jsonObj, "keycard-pairing")->toString();
result.keyUid = Json::getMandatoryProp(jsonObj, "key-uid")->toString();
@ -51,7 +51,7 @@ struct MultiAccount
/// TODO: investigate unhandled `photo-path` value
}
catch (std::exception e)
catch(std::exception e)
{
qWarning() << QString("Mapping MultiAccount failed: %1").arg(e.what());
}
@ -60,4 +60,4 @@ struct MultiAccount
}
};
}
} // namespace Status::Onboarding

View File

@ -2,8 +2,8 @@
#include <StatusGo/Accounts/accounts_types.h>
#include <QtCore>
#include <QStringLiteral>
#include <QtCore>
namespace GoAccounts = Status::StatusGo::Accounts;
@ -12,41 +12,42 @@ namespace Status::Constants
namespace Fleet
{
inline const auto Prod = u"eth.prod"_qs;
inline const auto Staging = u"eth.staging"_qs;
inline const auto Test = u"eth.test"_qs;
inline const auto WakuV2Prod = u"wakuv2.prod"_qs;
inline const auto WakuV2Test = u"wakuv2.test"_qs;
inline const auto GoWakuTest = u"go-waku.test"_qs;
}
inline const auto Prod = u"eth.prod"_qs;
inline const auto Staging = u"eth.staging"_qs;
inline const auto Test = u"eth.test"_qs;
inline const auto WakuV2Prod = u"wakuv2.prod"_qs;
inline const auto WakuV2Test = u"wakuv2.test"_qs;
inline const auto GoWakuTest = u"go-waku.test"_qs;
} // namespace Fleet
namespace FleetNodes
{
inline const auto Bootnodes = u"boot"_qs;
inline const auto Mailservers = u"mail"_qs;
inline const auto Rendezvous = u"rendezvous"_qs;
inline const auto Whisper = u"whisper"_qs;
inline const auto Waku = u"waku"_qs;
inline const auto LibP2P = u"libp2p"_qs;
inline const auto Websocket = u"websocket"_qs;
}
inline const auto Bootnodes = u"boot"_qs;
inline const auto Mailservers = u"mail"_qs;
inline const auto Rendezvous = u"rendezvous"_qs;
inline const auto Whisper = u"whisper"_qs;
inline const auto Waku = u"waku"_qs;
inline const auto LibP2P = u"libp2p"_qs;
inline const auto Websocket = u"websocket"_qs;
} // namespace FleetNodes
namespace General
{
inline const auto DefaultNetworkName = u"mainnet_rpc"_qs;
//const DEFAULT_NETWORKS_IDS* = @["mainnet_rpc", "testnet_rpc", "rinkeby_rpc", "goerli_rpc", "xdai_rpc", "poa_rpc" ]
inline const auto DefaultNetworkName = u"mainnet_rpc"_qs;
//const DEFAULT_NETWORKS_IDS* = @["mainnet_rpc", "testnet_rpc", "rinkeby_rpc", "goerli_rpc", "xdai_rpc", "poa_rpc" ]
inline const auto ZeroAddress = u"0x0000000000000000000000000000000000000000"_qs;
inline const auto ZeroAddress = u"0x0000000000000000000000000000000000000000"_qs;
inline const GoAccounts::DerivationPath PathWalletRoot{u"m/44'/60'/0'/0"_qs};
// EIP1581 Root Key, the extended key from which any whisper key/encryption key can be derived
inline const GoAccounts::DerivationPath PathEIP1581{u"m/43'/60'/1581'"_qs};
// BIP44-0 Wallet key, the default wallet key
inline const GoAccounts::DerivationPath PathDefaultWallet{PathWalletRoot.get() + u"/0"_qs};
// EIP1581 Chat Key 0, the default whisper key
inline const GoAccounts::DerivationPath PathWhisper{PathEIP1581.get() + u"/0'/0"_qs};
inline const GoAccounts::DerivationPath PathWalletRoot{u"m/44'/60'/0'/0"_qs};
// EIP1581 Root Key, the extended key from which any whisper key/encryption key can be derived
inline const GoAccounts::DerivationPath PathEIP1581{u"m/43'/60'/1581'"_qs};
// BIP44-0 Wallet key, the default wallet key
inline const GoAccounts::DerivationPath PathDefaultWallet{PathWalletRoot.get() + u"/0"_qs};
// EIP1581 Chat Key 0, the default whisper key
inline const GoAccounts::DerivationPath PathWhisper{PathEIP1581.get() + u"/0'/0"_qs};
inline const std::vector<GoAccounts::DerivationPath> AccountDefaultPaths {PathWalletRoot, PathEIP1581, PathWhisper, PathDefaultWallet};
}
inline const std::vector<GoAccounts::DerivationPath> AccountDefaultPaths{
PathWalletRoot, PathEIP1581, PathWhisper, PathDefaultWallet};
} // namespace General
}
} // namespace Status::Constants

View File

@ -20,7 +20,7 @@ public:
{
const auto it = getProp(object, field);
if (it == object.constEnd())
if(it == object.constEnd())
{
throw std::logic_error(QString("No field `%1`").arg(field).toStdString());
}
@ -29,4 +29,4 @@ public:
}
};
}
} // namespace Status

View File

@ -5,7 +5,7 @@
namespace Status::Constants
{
constexpr std::array SigningPhrases {
constexpr std::array SigningPhrases{
"area", "army", "atom", "aunt", "babe", "baby", "back", "bail", "bait", "bake", "ball", "band", "bank", "barn",
"base", "bass", "bath", "bead", "beak", "beam", "bean", "bear", "beat", "beef", "beer", "beet", "bell", "belt",
"bend", "bike", "bill", "bird", "bite", "blow", "blue", "boar", "boat", "body", "bolt", "bomb", "bone", "book",
@ -50,7 +50,6 @@ constexpr std::array SigningPhrases {
"veal", "veil", "vein", "vest", "vibe", "view", "vise", "wait", "wake", "walk", "wall", "wash", "wasp", "wave",
"wear", "weed", "week", "well", "west", "whip", "wife", "will", "wind", "wine", "wing", "wire", "wish", "wolf",
"wood", "wool", "word", "work", "worm", "wrap", "wren", "yard", "yarn", "yawl", "year", "yoga", "yoke", "yurt",
"zinc", "zone"
};
"zinc", "zone"};
}

View File

@ -15,70 +15,70 @@ NewAccountController::NewAccountController(AccountsServiceInterfacePtr accountsS
: m_accountsService(accountsService)
{
connect(this, &NewAccountController::passwordChanged, this, &NewAccountController::checkAndUpdateDataValidity);
connect(this, &NewAccountController::confirmationPasswordChanged, this, &NewAccountController::checkAndUpdateDataValidity);
connect(this,
&NewAccountController::confirmationPasswordChanged,
this,
&NewAccountController::checkAndUpdateDataValidity);
connect(this, &NewAccountController::nameChanged, this, &NewAccountController::checkAndUpdateDataValidity);
}
void NewAccountController::createAccount()
{
// TODO: fix this after moving SingalManager to StatusGo wrapper lib
QObject::connect(StatusGo::SignalsManager::instance(), &StatusGo::SignalsManager::nodeLogin, this, &NewAccountController::onNodeLogin);
QObject::connect(StatusGo::SignalsManager::instance(),
&StatusGo::SignalsManager::nodeLogin,
this,
&NewAccountController::onNodeLogin);
auto setupAccountFn = [this]() {
if(m_nameIsValid && m_passwordIsValid && m_confirmationPasswordIsValid) {
if(m_nameIsValid && m_passwordIsValid && m_confirmationPasswordIsValid)
{
auto genAccounts = m_accountsService->generatedAccounts();
if(genAccounts.size() > 0) {
if(m_accountsService->setupAccountAndLogin(genAccounts[0].id, m_password, m_name))
return;
if(genAccounts.size() > 0)
{
if(m_accountsService->setupAccountAndLogin(genAccounts[0].id, m_password, m_name)) return;
}
}
};
// TODO: refactor StatusGo wrapper to work with futures instead of SignalManager
m_createAccountFuture = QtConcurrent::run(setupAccountFn)
.then([]{ /*Nothing, we expect status-go events*/ })
.onFailed([this] {
emit accountCreationError();
})
.onCanceled([this] {
emit accountCreationError();
});
.then([] { /*Nothing, we expect status-go events*/ })
.onFailed([this] { emit accountCreationError(); })
.onCanceled([this] { emit accountCreationError(); });
}
const QString &NewAccountController::password() const
const QString& NewAccountController::password() const
{
return m_password;
}
void NewAccountController::setPassword(const QString &newPassword)
void NewAccountController::setPassword(const QString& newPassword)
{
if (m_password == newPassword)
return;
if(m_password == newPassword) return;
m_password = newPassword;
emit passwordChanged();
}
const QString &NewAccountController::confirmationPassword() const
const QString& NewAccountController::confirmationPassword() const
{
return m_confirmationPassword;
}
void NewAccountController::setConfirmationPassword(const QString &newConfirmationPassword)
void NewAccountController::setConfirmationPassword(const QString& newConfirmationPassword)
{
if (m_confirmationPassword == newConfirmationPassword)
return;
if(m_confirmationPassword == newConfirmationPassword) return;
m_confirmationPassword = newConfirmationPassword;
emit confirmationPasswordChanged();
}
const QString &NewAccountController::name() const
const QString& NewAccountController::name() const
{
return m_name;
}
void NewAccountController::setName(const QString &newName)
void NewAccountController::setName(const QString& newName)
{
if (m_name == newName)
return;
if(m_name == newName) return;
m_name = newName;
emit nameChanged();
}
@ -109,19 +109,22 @@ void NewAccountController::onNodeLogin(const QString& error)
void NewAccountController::checkAndUpdateDataValidity()
{
auto passwordValid = m_password.length() >= 6;
if(passwordValid != m_passwordIsValid) {
if(passwordValid != m_passwordIsValid)
{
m_passwordIsValid = passwordValid;
emit passwordIsValidChanged();
}
auto confirmationPasswordValid = m_password == m_confirmationPassword;
if(confirmationPasswordValid != m_confirmationPasswordIsValid) {
if(confirmationPasswordValid != m_confirmationPasswordIsValid)
{
m_confirmationPasswordIsValid = confirmationPasswordValid;
emit confirmationPasswordIsValidChanged();
}
auto nameValid = m_name.length() >= 10;
if(nameValid != m_nameIsValid) {
if(nameValid != m_nameIsValid)
{
m_nameIsValid = nameValid;
emit nameIsValidChanged();
}

View File

@ -20,7 +20,7 @@ class ServiceInterface;
*
* \todo shared functionality should be moved to Common library (e.g. Name/Picture Validation)
*/
class NewAccountController: public QObject
class NewAccountController : public QObject
{
Q_OBJECT
@ -28,10 +28,12 @@ class NewAccountController: public QObject
QML_UNCREATABLE("Created and owned externally")
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
Q_PROPERTY(QString confirmationPassword READ confirmationPassword WRITE setConfirmationPassword NOTIFY confirmationPasswordChanged)
Q_PROPERTY(QString confirmationPassword READ confirmationPassword WRITE setConfirmationPassword NOTIFY
confirmationPasswordChanged)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(bool passwordIsValid READ passwordIsValid NOTIFY passwordIsValidChanged)
Q_PROPERTY(bool confirmationPasswordIsValid READ confirmationPasswordIsValid NOTIFY confirmationPasswordIsValidChanged)
Q_PROPERTY(
bool confirmationPasswordIsValid READ confirmationPasswordIsValid NOTIFY confirmationPasswordIsValidChanged)
Q_PROPERTY(bool nameIsValid READ nameIsValid NOTIFY nameIsValidChanged)
public:
@ -39,14 +41,14 @@ public:
Q_INVOKABLE void createAccount();
const QString &password() const;
void setPassword(const QString &newPassword);
const QString& password() const;
void setPassword(const QString& newPassword);
const QString &confirmationPassword() const;
void setConfirmationPassword(const QString &newConfirmationPassword);
const QString& confirmationPassword() const;
void setConfirmationPassword(const QString& newConfirmationPassword);
const QString &name() const;
void setName(const QString &newName);
const QString& name() const;
void setName(const QString& newName);
bool passwordIsValid() const;
bool confirmationPasswordIsValid() const;

View File

@ -8,7 +8,8 @@
#include <Helpers/helpers.h>
namespace Status::Onboarding {
namespace Status::Onboarding
{
namespace StatusGo = Status::StatusGo;
@ -16,15 +17,20 @@ OnboardingController::OnboardingController(AccountsServiceInterfacePtr accountsS
: QObject(nullptr)
, m_accountsService(std::move(accountsService))
{
{ // Init accounts
{ // Init accounts
std::vector<std::shared_ptr<UserAccount>> accounts;
for(auto &account : getOpenedAccounts()) {
accounts.push_back(Helpers::makeSharedQObject<UserAccount>(std::make_unique<MultiAccount>(std::move(account))));
for(auto& account : getOpenedAccounts())
{
accounts.push_back(
Helpers::makeSharedQObject<UserAccount>(std::make_unique<MultiAccount>(std::move(account))));
}
m_accounts = Helpers::makeSharedQObject<UserAccountsModel>(std::move(accounts));
}
connect(StatusGo::SignalsManager::instance(), &StatusGo::SignalsManager::nodeLogin, this, &OnboardingController::onLogin);
connect(StatusGo::SignalsManager::instance(),
&StatusGo::SignalsManager::nodeLogin,
this,
&OnboardingController::onLogin);
}
OnboardingController::~OnboardingController()
@ -50,16 +56,15 @@ void OnboardingController::login(QObject* user, const QString& password)
auto account = qobject_cast<UserAccount*>(user);
assert(account != nullptr);
auto error = m_accountsService->login(account->accountData(), password);
if(!error.isEmpty())
emit accountLoginError(error);
if(!error.isEmpty()) emit accountLoginError(error);
}
UserAccountsModel *OnboardingController::accounts() const
UserAccountsModel* OnboardingController::accounts() const
{
return m_accounts.get();
}
NewAccountController *OnboardingController::initNewAccountController()
NewAccountController* OnboardingController::initNewAccountController()
{
m_newAccountController = std::make_unique<NewAccountController>(m_accountsService);
emit newAccountControllerChanged();
@ -72,7 +77,7 @@ void OnboardingController::terminateNewAccountController()
emit newAccountControllerChanged();
}
NewAccountController *OnboardingController::newAccountController() const
NewAccountController* OnboardingController::newAccountController() const
{
return m_newAccountController.get();
}
@ -82,4 +87,4 @@ AccountsServiceInterfacePtr OnboardingController::accountsService() const
return m_accountsService;
}
}
} // namespace Status::Onboarding

View File

@ -22,8 +22,7 @@ class NewAccountController;
* \todo don't use DTOs in controllers, use QObjects directly
* \todo make dependency on SignalManager explicit. Now it is hidden.
*/
class OnboardingController final : public QObject
, public std::enable_shared_from_this<OnboardingController>
class OnboardingController final : public QObject, public std::enable_shared_from_this<OnboardingController>
{
Q_OBJECT
@ -44,11 +43,11 @@ public:
/// TODO: \a user should be of type \c UserAccount but this doesn't work with Qt6 CMake API. Investigate and fix later on
Q_INVOKABLE void login(QObject* user, const QString& password);
UserAccountsModel *accounts() const;
UserAccountsModel* accounts() const;
Q_INVOKABLE Status::Onboarding::NewAccountController *initNewAccountController();
Q_INVOKABLE Status::Onboarding::NewAccountController* initNewAccountController();
Q_INVOKABLE void terminateNewAccountController();
NewAccountController *newAccountController() const;
NewAccountController* newAccountController() const;
std::shared_ptr<AccountsServiceInterface> accountsService() const;
signals:
@ -72,4 +71,4 @@ private:
std::unique_ptr<NewAccountController> m_newAccountController;
};
}
} // namespace Status::Onboarding

View File

@ -10,20 +10,20 @@ namespace AppCore = Status::ApplicationCore;
namespace fs = std::filesystem;
namespace Status::Onboarding {
namespace Status::Onboarding
{
OnboardingModule::OnboardingModule(const fs::path& userDataPath, QObject *parent)
OnboardingModule::OnboardingModule(const fs::path& userDataPath, QObject* parent)
: OnboardingModule{parent}
{
m_userDataPath = userDataPath;
initWithUserDataPath(m_userDataPath);
}
OnboardingModule::OnboardingModule(QObject *parent)
OnboardingModule::OnboardingModule(QObject* parent)
: QObject{parent}
, m_accountsService(std::make_shared<AccountsService>())
{
}
{ }
OnboardingController* OnboardingModule::controller() const
{
@ -32,20 +32,21 @@ OnboardingController* OnboardingModule::controller() const
void OnboardingModule::componentComplete()
{
try {
try
{
initWithUserDataPath(m_userDataPath);
} catch(const std::exception &e) {
}
catch(const std::exception& e)
{
qCritical() << "OnboardingModule: failed to initialize";
}
}
void OnboardingModule::initWithUserDataPath(const fs::path &path)
void OnboardingModule::initWithUserDataPath(const fs::path& path)
{
auto result = m_accountsService->init(path);
if(!result)
throw std::runtime_error(std::string("Failed to initialize OnboadingService") + path.string());
m_controller = Helpers::makeSharedQObject<OnboardingController>(
m_accountsService);
if(!result) throw std::runtime_error(std::string("Failed to initialize OnboadingService") + path.string());
m_controller = Helpers::makeSharedQObject<OnboardingController>(m_accountsService);
emit controllerChanged();
}
@ -54,13 +55,12 @@ const QString OnboardingModule::userDataPath() const
return QString::fromStdString(m_userDataPath.string());
}
void OnboardingModule::setUserDataPath(const QString &newUserDataPath)
void OnboardingModule::setUserDataPath(const QString& newUserDataPath)
{
auto newVal = newUserDataPath.toStdString();
if (m_userDataPath.compare(newVal) == 0)
return;
if(m_userDataPath.compare(newVal) == 0) return;
m_userDataPath = newVal;
emit userDataPathChanged();
}
}
} // namespace Status::Onboarding

View File

@ -9,7 +9,8 @@
namespace fs = std::filesystem;
namespace Status::Onboarding {
namespace Status::Onboarding
{
class AccountsService;
@ -31,16 +32,16 @@ class OnboardingModule : public QObject, public QQmlParserStatus
Q_PROPERTY(QString userDataPath READ userDataPath WRITE setUserDataPath NOTIFY userDataPathChanged REQUIRED)
public:
explicit OnboardingModule(const fs::path& userDataPath, QObject *parent = nullptr);
explicit OnboardingModule(QObject *parent = nullptr);
explicit OnboardingModule(const fs::path& userDataPath, QObject* parent = nullptr);
explicit OnboardingModule(QObject* parent = nullptr);
OnboardingController* controller() const;
const QString userDataPath() const;
void setUserDataPath(const QString &newUserDataPath);
void setUserDataPath(const QString& newUserDataPath);
/// QML inteface
void classBegin() override {};
void classBegin() override{};
void componentComplete() override;
signals:
@ -48,9 +49,8 @@ signals:
void userDataPathChanged();
private:
/// Throws exceptions
void initWithUserDataPath(const fs::path &path);
void initWithUserDataPath(const fs::path& path);
// TODO: plain object after refactoring shared_ptr requirement for now
std::shared_ptr<AccountsService> m_accountsService;
@ -59,4 +59,4 @@ private:
fs::path m_userDataPath;
};
}
} // namespace Status::Onboarding

View File

@ -8,15 +8,14 @@ namespace Status::Onboarding
UserAccount::UserAccount(std::unique_ptr<MultiAccount> data)
: QObject()
, m_data(std::move(data))
{
}
{ }
const QString &UserAccount::name() const
const QString& UserAccount::name() const
{
return m_data->name;
}
const MultiAccount &UserAccount::accountData() const
const MultiAccount& UserAccount::accountData() const
{
return *m_data;
}
@ -27,9 +26,7 @@ void UserAccount::updateAccountData(const MultiAccount& newData)
*m_data = newData;
if(newData.name != m_data->name)
notifyUpdates.push_back([this]() { emit nameChanged(); });
if(newData.name != m_data->name) notifyUpdates.push_back([this]() { emit nameChanged(); });
}
}
} // namespace Status::Onboarding

View File

@ -13,7 +13,7 @@ class MultiAccount;
* @see OnboardingController
* @see UserAccountsModel
*/
class UserAccount: public QObject
class UserAccount : public QObject
{
Q_OBJECT
QML_ELEMENT
@ -23,7 +23,7 @@ class UserAccount: public QObject
public:
explicit UserAccount(std::unique_ptr<MultiAccount> data);
const QString &name() const;
const QString& name() const;
const MultiAccount& accountData() const;
void updateAccountData(const MultiAccount& newData);
@ -35,4 +35,4 @@ private:
std::unique_ptr<MultiAccount> m_data;
};
}
} // namespace Status::Onboarding

View File

@ -2,25 +2,19 @@
#include <QObject>
namespace Status::Onboarding {
namespace Status::Onboarding
{
UserAccountsModel::UserAccountsModel(const std::vector<std::shared_ptr<UserAccount>> accounts, QObject* parent)
: QAbstractListModel(parent)
, m_accounts(std::move(accounts))
{
}
{ }
UserAccountsModel::~UserAccountsModel()
{
}
UserAccountsModel::~UserAccountsModel() { }
QHash<int, QByteArray> UserAccountsModel::roleNames() const
{
static QHash<int, QByteArray> roles{
{Name, "name"},
{Account, "account"}
};
static QHash<int, QByteArray> roles{{Name, "name"}, {Account, "account"}};
return roles;
}
@ -32,14 +26,14 @@ int UserAccountsModel::rowCount(const QModelIndex& parent) const
QVariant UserAccountsModel::data(const QModelIndex& index, int role) const
{
if(!QAbstractItemModel::checkIndex(index))
return QVariant();
if(!QAbstractItemModel::checkIndex(index)) return QVariant();
switch(static_cast<ModelRole>(role)) {
case Name: return QVariant::fromValue(m_accounts[index.row()].get()->name());
case Account: return QVariant::fromValue<QObject*>(m_accounts[index.row()].get());
default: return QVariant();
switch(static_cast<ModelRole>(role))
{
case Name: return QVariant::fromValue(m_accounts[index.row()].get()->name());
case Account: return QVariant::fromValue<QObject*>(m_accounts[index.row()].get());
default: return QVariant();
}
}
}
} // namespace Status::Onboarding

View File

@ -4,7 +4,8 @@
#include <QAbstractListModel>
namespace Status::Onboarding {
namespace Status::Onboarding
{
/// \todo Replace it with \c QObjectVectorModel
class UserAccountsModel : public QAbstractListModel
@ -14,12 +15,13 @@ class UserAccountsModel : public QAbstractListModel
QML_ELEMENT
QML_UNCREATABLE("Created by OnboardingController")
enum ModelRole {
enum ModelRole
{
Name = Qt::UserRole + 1,
Account
};
public:
public:
explicit UserAccountsModel(const std::vector<std::shared_ptr<UserAccount>> accounts, QObject* parent = nullptr);
~UserAccountsModel();
QHash<int, QByteArray> roleNames() const override;
@ -30,4 +32,4 @@ private:
const std::vector<std::shared_ptr<UserAccount>> m_accounts;
};
}
} // namespace Status::Onboarding

View File

@ -2,7 +2,8 @@
#include <string>
namespace Status::Testing::Constants {
namespace Status::Testing::Constants
{
inline constexpr auto userDataDirName = "StatusTest";
inline constexpr auto statusGoDataDirName = "data";
@ -12,4 +13,4 @@ inline constexpr auto qtDataDirName = "qt";
inline constexpr auto keystoreDataDirName = "keystore";
inline constexpr auto globalSettingsFileName = "global";
}
} // namespace Status::Testing::Constants

View File

@ -20,11 +20,12 @@ namespace Accounts = Status::StatusGo::Accounts;
namespace fs = std::filesystem;
namespace Status::Testing {
namespace Status::Testing
{
ScopedTestAccount::ScopedTestAccount(const std::string &tempTestSubfolderName,
const QString &accountName,
const QString &accountPassword)
ScopedTestAccount::ScopedTestAccount(const std::string& tempTestSubfolderName,
const QString& accountName,
const QString& accountPassword)
: m_fusedTestFolder{std::make_unique<AutoCleanTempTestDir>(tempTestSubfolderName)}
, m_accountName(accountName)
, m_accountPassword(accountPassword)
@ -41,7 +42,9 @@ ScopedTestAccount::ScopedTestAccount(const std::string &tempTestSubfolderName,
auto accountsService = std::make_shared<Onboarding::AccountsService>();
auto result = accountsService->init(m_testFolderPath);
if(!result)
{
throw std::runtime_error("ScopedTestAccount - Failed to create temporary test account");
}
// TODO refactor and merge account creation events with login into Onboarding controller
//
@ -51,41 +54,59 @@ ScopedTestAccount::ScopedTestAccount(const std::string &tempTestSubfolderName,
// Beware, smartpointer is a requirement
m_onboarding = Helpers::makeSharedQObject<Onboarding::OnboardingController>(accountsService);
if(m_onboarding->getOpenedAccounts().size() != 0)
{
throw std::runtime_error("ScopedTestAccount - already have opened account");
}
int accountLoggedInCount = 0;
QObject::connect(m_onboarding.get(), &Onboarding::OnboardingController::accountLoggedIn, [&accountLoggedInCount]() {
accountLoggedInCount++;
});
bool accountLoggedInError = false;
QObject::connect(m_onboarding.get(), &Onboarding::OnboardingController::accountLoginError, [&accountLoggedInError]() {
accountLoggedInError = true;
});
QObject::connect(m_onboarding.get(),
&Onboarding::OnboardingController::accountLoginError,
[&accountLoggedInError]() { accountLoggedInError = true; });
// Create Accounts
auto genAccounts = accountsService->generatedAccounts();
if(genAccounts.size() == 0)
{
throw std::runtime_error("ScopedTestAccount - missing generated accounts");
}
if(accountsService->isFirstTimeAccountLogin())
{
throw std::runtime_error("ScopedTestAccount - Service::isFirstTimeAccountLogin returned true");
}
if(!accountsService->setupAccountAndLogin(genAccounts[0].id, m_accountPassword, m_accountName))
{
throw std::runtime_error("ScopedTestAccount - Service::setupAccountAndLogin failed");
}
if(!accountsService->isFirstTimeAccountLogin())
{
throw std::runtime_error("ScopedTestAccount - Service::isFirstTimeAccountLogin returned false");
}
if(!accountsService->getLoggedInAccount().isValid())
{
throw std::runtime_error("ScopedTestAccount - newly created account is not valid");
}
if(accountsService->getLoggedInAccount().name != accountName)
{
throw std::runtime_error("ScopedTestAccount - newly created account has a wrong name");
processMessages(2000, [accountLoggedInCount]() {
return accountLoggedInCount == 0;
});
}
processMessages(2000, [accountLoggedInCount]() { return accountLoggedInCount == 0; });
if(accountLoggedInCount != 1)
{
throw std::runtime_error("ScopedTestAccount - missing confirmation of account creation");
}
if(accountLoggedInError)
{
throw std::runtime_error("ScopedTestAccount - account loggedin error");
}
}
ScopedTestAccount::~ScopedTestAccount()
@ -94,12 +115,14 @@ ScopedTestAccount::~ScopedTestAccount()
m_onboarding->accountsService()->deleteMultiAccount(rootAccount);
}
void ScopedTestAccount::processMessages(size_t maxWaitTimeMillis, std::function<bool()> shouldWaitUntilTimeout) {
void ScopedTestAccount::processMessages(size_t maxWaitTimeMillis, std::function<bool()> shouldWaitUntilTimeout)
{
using namespace std::chrono_literals;
std::chrono::milliseconds maxWaitTime{maxWaitTimeMillis};
auto iterationSleepTime = 2ms;
auto remainingIterations = maxWaitTime/iterationSleepTime;
while (remainingIterations-- > 0 && shouldWaitUntilTimeout()) {
auto remainingIterations = maxWaitTime / iterationSleepTime;
while(remainingIterations-- > 0 && shouldWaitUntilTimeout())
{
std::this_thread::sleep_for(iterationSleepTime);
QCoreApplication::sendPostedEvents();
@ -109,44 +132,46 @@ void ScopedTestAccount::processMessages(size_t maxWaitTimeMillis, std::function<
void ScopedTestAccount::logOut()
{
if(Status::StatusGo::Accounts::logout().containsError())
{
throw std::runtime_error("ScopedTestAccount - failed logging out");
}
}
Accounts::ChatOrWalletAccount ScopedTestAccount::firstChatAccount()
{
auto accounts = Accounts::getAccounts();
auto chatIt = std::find_if(accounts.begin(), accounts.end(), [](const auto& a) {
return a.isChat;
});
auto chatIt = std::find_if(accounts.begin(), accounts.end(), [](const auto& a) { return a.isChat; });
if(chatIt == accounts.end())
{
throw std::runtime_error("ScopedTestAccount::chatAccount: account not found");
}
return *chatIt;
}
Accounts::ChatOrWalletAccount ScopedTestAccount::firstWalletAccount()
{
auto accounts = Accounts::getAccounts();
auto walletIt = std::find_if(accounts.begin(), accounts.end(), [](const auto& a) {
return a.isWallet;
});
auto walletIt = std::find_if(accounts.begin(), accounts.end(), [](const auto& a) { return a.isWallet; });
if(walletIt == accounts.end())
{
throw std::runtime_error("ScopedTestAccount::firstWalletAccount: account not found");
}
return *walletIt;
}
const Onboarding::MultiAccount &ScopedTestAccount::loggedInAccount() const
const Onboarding::MultiAccount& ScopedTestAccount::loggedInAccount() const
{
return m_onboarding->accountsService()->getLoggedInAccount();
}
Onboarding::OnboardingController *ScopedTestAccount::onboardingController() const
Onboarding::OnboardingController* ScopedTestAccount::onboardingController() const
{
return m_onboarding.get();
}
const std::filesystem::path &ScopedTestAccount::fusedTestFolder() const
const std::filesystem::path& ScopedTestAccount::fusedTestFolder() const
{
return m_testFolderPath;
}
}
} // namespace Status::Testing

View File

@ -4,36 +4,39 @@
#include <StatusGo/Utils.h>
#include <string>
#include <filesystem>
#include <string>
#include <QString>
class QCoreApplication;
namespace Status::Onboarding {
class OnboardingController;
class MultiAccount;
}
namespace Status::Onboarding
{
class OnboardingController;
class MultiAccount;
} // namespace Status::Onboarding
namespace Wallet = Status::StatusGo::Wallet;
namespace Accounts = Status::StatusGo::Accounts;
namespace GoUtils = Status::StatusGo::Utils;
namespace Status::Testing {
namespace Status::Testing
{
class AutoCleanTempTestDir;
class ScopedTestAccount final {
class ScopedTestAccount final
{
public:
/*!
* \brief Create and logs in a new test account
* \param tempTestSubfolderName subfolder name of the temporary test folder where to initalize user data \see AutoCleanTempTestDir
* \todo make it more flexible by splitting into create account, login and wait for events
*/
explicit ScopedTestAccount(const std::string &tempTestSubfolderName,
const QString &accountName = defaultAccountName,
const QString &accountPassword = defaultAccountPassword);
explicit ScopedTestAccount(const std::string& tempTestSubfolderName,
const QString& accountName = defaultAccountName,
const QString& accountPassword = defaultAccountPassword);
~ScopedTestAccount();
void processMessages(size_t millis, std::function<bool()> shouldWaitUntilTimeout);
@ -42,10 +45,16 @@ public:
static Accounts::ChatOrWalletAccount firstChatAccount();
static Accounts::ChatOrWalletAccount firstWalletAccount();
/// Root account
const Status::Onboarding::MultiAccount &loggedInAccount() const;
const Status::Onboarding::MultiAccount& loggedInAccount() const;
QString password() const { return m_accountPassword; };
StatusGo::HashedPassword hashedPassword() const { return GoUtils::hashPassword(m_accountPassword); };
QString password() const
{
return m_accountPassword;
};
StatusGo::HashedPassword hashedPassword() const
{
return GoUtils::hashPassword(m_accountPassword);
};
Status::Onboarding::OnboardingController* onboardingController() const;
@ -65,4 +74,4 @@ private:
static constexpr auto defaultAccountPassword = "test_pwd*";
};
}
} // namespace Status::Testing

View File

@ -18,7 +18,7 @@ namespace Status::Testing
class AccountsServiceMock final : public Onboarding::AccountsServiceInterface
{
public:
virtual ~AccountsServiceMock() override {};
virtual ~AccountsServiceMock() override{};
MOCK_METHOD(bool, init, (const fs::path&), (override));
MOCK_METHOD(std::vector<Onboarding::MultiAccount>, openAndListAccounts, (), (override));
@ -34,4 +34,4 @@ public:
MOCK_METHOD(void, deleteMultiAccount, (const Onboarding::MultiAccount&), (override));
};
}
} // namespace Status::Testing

View File

@ -1,7 +1,7 @@
#include "ServiceMock.h"
#include <IOTestHelpers.h>
#include <Constants.h>
#include <IOTestHelpers.h>
#include <StatusGo/Accounts/Accounts.h>
@ -15,7 +15,8 @@ namespace Onboarding = Status::Onboarding;
namespace fs = std::filesystem;
namespace Status::Testing {
namespace Status::Testing
{
class AccountsService : public ::testing::Test
{
@ -23,19 +24,20 @@ protected:
std::unique_ptr<Onboarding::AccountsService> m_accountsService;
std::unique_ptr<Testing::AutoCleanTempTestDir> m_fusedTestFolder;
void SetUp() override {
void SetUp() override
{
m_fusedTestFolder = std::make_unique<Testing::AutoCleanTempTestDir>("TestAccountsService");
m_accountsService = std::make_unique<Onboarding::AccountsService>();
m_accountsService->init(m_fusedTestFolder->tempFolder() / Constants::statusGoDataDirName);
}
void TearDown() override {
void TearDown() override
{
m_fusedTestFolder.reset();
m_accountsService.reset();
}
};
TEST_F(AccountsService, GeneratedAccounts)
{
auto genAccounts = m_accountsService->generatedAccounts();
@ -51,13 +53,16 @@ TEST_F(AccountsService, GeneratedAccounts)
}
}
TEST_F(AccountsService, DISABLED_GenerateAlias) // temporary disabled till we see what's happening on the status-go side since it doesn't return aliases for any pk
TEST_F(
AccountsService,
DISABLED_GenerateAlias) // temporary disabled till we see what's happening on the status-go side since it doesn't return aliases for any pk
{
QString testPubKey = "0x04487f44bac3e90825bfa9720148308cb64835bebb7e888f519cebc127223187067629f8b70d0661a35d4af6516b225286";
QString testPubKey =
"0x04487f44bac3e90825bfa9720148308cb64835bebb7e888f519cebc127223187067629f8b70d0661a35d4af6516b225286";
auto alias = m_accountsService->generateAlias(testPubKey);
ASSERT_NE(alias, QString(""));
}
} // namespace
} // namespace Status::Testing

View File

@ -17,7 +17,8 @@ namespace Onboarding = Status::Onboarding;
namespace fs = std::filesystem;
namespace Status::Testing {
namespace Status::Testing
{
class LoginTest : public ::testing::Test
{
@ -27,20 +28,24 @@ protected:
std::unique_ptr<Onboarding::AccountsService> m_accountsService;
std::unique_ptr<Testing::AutoCleanTempTestDir> m_fusedTestFolder;
static void SetUpTestSuite() {
static void SetUpTestSuite()
{
m_accountsServiceMock = std::make_shared<AccountsServiceMock>();
}
static void TearDownTestSuite() {
static void TearDownTestSuite()
{
m_accountsServiceMock.reset();
}
void SetUp() override {
void SetUp() override
{
m_fusedTestFolder = std::make_unique<Testing::AutoCleanTempTestDir>("LoginTest");
m_accountsService = std::make_unique<Onboarding::AccountsService>();
m_accountsService->init(m_fusedTestFolder->tempFolder() / Constants::statusGoDataDirName);
}
void TearDown() override {
void TearDown() override
{
m_fusedTestFolder.release();
m_accountsService.release();
}
@ -54,4 +59,4 @@ TEST_F(LoginTest, DISABLED_TestLoginController)
auto controller = Helpers::makeSharedQObject<Onboarding::OnboardingController>(m_accountsServiceMock);
}
} // namespace
} // namespace Status::Testing

View File

@ -7,8 +7,8 @@
#include <Onboarding/Accounts/AccountsService.h>
#include <Onboarding/OnboardingController.h>
#include <StatusGo/SignalsManager.h>
#include <StatusGo/Accounts/Accounts.h>
#include <StatusGo/SignalsManager.h>
#include <ScopedTestAccount.h>
@ -21,7 +21,8 @@ namespace Onboarding = Status::Onboarding;
namespace fs = std::filesystem;
namespace Status::Testing {
namespace Status::Testing
{
static std::unique_ptr<Onboarding::AccountsService> m_accountsServiceMock;
@ -89,8 +90,9 @@ TEST(OnboardingModule, TestCreateAndLoginAccountEndToEnd)
using namespace std::chrono_literals;
auto maxWaitTime = 2000ms;
auto iterationSleepTime = 2ms;
auto remainingIterations = maxWaitTime/iterationSleepTime;
while (remainingIterations-- > 0 && accountLoggedInCount == 0) {
auto remainingIterations = maxWaitTime / iterationSleepTime;
while(remainingIterations-- > 0 && accountLoggedInCount == 0)
{
std::this_thread::sleep_for(iterationSleepTime);
QCoreApplication::sendPostedEvents();
@ -110,20 +112,23 @@ TEST(OnboardingModule, TestLoginEndToEnd)
// Create test account and login
//
bool createAndLogin = false;
QObject::connect(StatusGo::SignalsManager::instance(), &StatusGo::SignalsManager::nodeLogin, [&createAndLogin](const QString& error) {
if(error.isEmpty()) {
if(createAndLogin) {
createAndLogin = false;
} else
createAndLogin = true;
}
});
QObject::connect(StatusGo::SignalsManager::instance(),
&StatusGo::SignalsManager::nodeLogin,
[&createAndLogin](const QString& error) {
if(error.isEmpty())
{
if(createAndLogin)
{
createAndLogin = false;
}
else
createAndLogin = true;
}
});
constexpr auto accountName = "TestLoginAccountName";
ScopedTestAccount testAccount(test_info_->name(), accountName);
testAccount.processMessages(1000, [createAndLogin]() {
return !createAndLogin;
});
testAccount.processMessages(1000, [createAndLogin]() { return !createAndLogin; });
ASSERT_TRUE(createAndLogin);
testAccount.logOut();
@ -148,14 +153,15 @@ TEST(OnboardingModule, TestLoginEndToEnd)
accountLoggedInCount++;
});
bool accountLoggedInError = false;
QObject::connect(onboarding.get(), &Onboarding::OnboardingController::accountLoginError,
QObject::connect(onboarding.get(),
&Onboarding::OnboardingController::accountLoginError,
[&accountLoggedInError](const QString& error) {
accountLoggedInError = true;
qDebug() << "Failed logging in in test" << test_info_->name() << "with error:" << error;
}
);
accountLoggedInError = true;
qDebug() << "Failed logging in in test" << test_info_->name() << "with error:" << error;
});
auto ourAccountRes = std::find_if(accounts.begin(), accounts.end(), [accountName](const auto &a) { return a.name == accountName; });
auto ourAccountRes =
std::find_if(accounts.begin(), accounts.end(), [accountName](const auto& a) { return a.name == accountName; });
auto errorString = accountsService->login(*ourAccountRes, testAccount.password());
ASSERT_EQ(errorString.length(), 0);
@ -186,13 +192,16 @@ TEST(OnboardingModule, TestLoginEndToEnd_WrongPassword)
});
bool accountLoggedInError = false;
QString loginErrorMessage;
QObject::connect(onboarding.get(), &Onboarding::OnboardingController::accountLoginError,
QObject::connect(onboarding.get(),
&Onboarding::OnboardingController::accountLoginError,
[&loginErrorMessage, &accountLoggedInError](const QString& error) {
accountLoggedInError = true;
loginErrorMessage = error;
});
accountLoggedInError = true;
loginErrorMessage = error;
});
auto ourAccountRes = std::find_if(accounts.begin(), accounts.end(), [testRootAccountName](const auto &a) { return a.name == testRootAccountName; });
auto ourAccountRes = std::find_if(accounts.begin(), accounts.end(), [testRootAccountName](const auto& a) {
return a.name == testRootAccountName;
});
auto errorString = accountsService->login(*ourAccountRes, testAccount.password() + "extra");
ASSERT_EQ(errorString.length(), 0);
@ -204,4 +213,4 @@ TEST(OnboardingModule, TestLoginEndToEnd_WrongPassword)
ASSERT_EQ(loginErrorMessage, "file is not a database");
}
} // namespace
} // namespace Status::Testing

View File

@ -7,16 +7,15 @@
const int NUMBER_OF_ADDRESSES_TO_GENERATE = 5;
const int MNEMONIC_PHRASE_LENGTH = 12;
namespace Status::StatusGo::Accounts {
namespace Status::StatusGo::Accounts
{
RpcResponse<QJsonArray> generateAddresses(const std::vector<Accounts::DerivationPath>& paths)
{
QJsonObject payload{
{"n", NUMBER_OF_ADDRESSES_TO_GENERATE},
{"mnemonicPhraseLength", MNEMONIC_PHRASE_LENGTH},
{"bip32Passphrase", ""},
{"paths", Utils::toJsonArray(paths)}
};
QJsonObject payload{{"n", NUMBER_OF_ADDRESSES_TO_GENERATE},
{"mnemonicPhraseLength", MNEMONIC_PHRASE_LENGTH},
{"bip32Passphrase", ""},
{"paths", Utils::toJsonArray(paths)}};
try
{
@ -30,13 +29,13 @@ RpcResponse<QJsonArray> generateAddresses(const std::vector<Accounts::Derivation
return Utils::buildPrivateRPCResponse(jsonResult);
}
catch (std::exception& e)
catch(std::exception& e)
{
auto response = RpcResponse<QJsonArray>(QJsonArray());
response.error.message = QObject::tr("an error generating address occurred, msg: %1").arg(e.what());
return response;
}
catch (...)
catch(...)
{
auto response = RpcResponse<QJsonArray>(QJsonArray());
response.error.message = QObject::tr("an error generating address occurred");
@ -56,7 +55,7 @@ RpcResponse<QString> generateAlias(const QString& publicKey)
return Utils::buildPrivateRPCResponse(alias);
}
catch (...)
catch(...)
{
auto response = RpcResponse<QString>(QString());
response.error.message = QObject::tr("an error generating alias occurred");
@ -64,13 +63,11 @@ RpcResponse<QString> generateAlias(const QString& publicKey)
}
}
RpcResponse<QJsonObject> storeDerivedAccounts(const QString& id, const HashedPassword& password, const std::vector<Accounts::DerivationPath>& paths)
RpcResponse<QJsonObject> storeDerivedAccounts(const QString& id,
const HashedPassword& password,
const std::vector<Accounts::DerivationPath>& paths)
{
QJsonObject payload{
{"accountID", id},
{"paths", Utils::toJsonArray(paths)},
{"password", password.get()}
};
QJsonObject payload{{"accountID", id}, {"paths", Utils::toJsonArray(paths)}, {"password", password.get()}};
try
{
@ -86,13 +83,13 @@ RpcResponse<QJsonObject> storeDerivedAccounts(const QString& id, const HashedPas
rpcResponse.error = Utils::getRPCErrorInJson(jsonResult).value_or(RpcError());
return rpcResponse;
}
catch (std::exception& e)
catch(std::exception& e)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error storing derived accounts occurred, msg: %1").arg(e.what());
return response;
}
catch (...)
catch(...)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error storing derived accounts occurred");
@ -102,10 +99,7 @@ RpcResponse<QJsonObject> storeDerivedAccounts(const QString& id, const HashedPas
RpcResponse<QJsonObject> storeAccount(const QString& id, const HashedPassword& password)
{
QJsonObject payload{
{"accountID", id},
{"password", password.get()}
};
QJsonObject payload{{"accountID", id}, {"password", password.get()}};
try
{
@ -121,13 +115,13 @@ RpcResponse<QJsonObject> storeAccount(const QString& id, const HashedPassword& p
rpcResponse.error = Utils::getRPCErrorInJson(jsonResult).value_or(RpcError());
return rpcResponse;
}
catch (std::exception& e)
catch(std::exception& e)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error storing account occurred, msg: %1").arg(e.what());
return response;
}
catch (...)
catch(...)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error storing account occurred");
@ -135,8 +129,10 @@ RpcResponse<QJsonObject> storeAccount(const QString& id, const HashedPassword& p
}
}
bool saveAccountAndLogin(const HashedPassword& password, const QJsonObject& account,
const QJsonArray& subaccounts, const QJsonObject& settings,
bool saveAccountAndLogin(const HashedPassword& password,
const QJsonObject& account,
const QJsonArray& subaccounts,
const QJsonObject& settings,
const QJsonObject& nodeConfig)
{
try
@ -154,9 +150,13 @@ bool saveAccountAndLogin(const HashedPassword& password, const QJsonObject& acco
}
return !Utils::getRPCErrorInJson(jsonResult).has_value();
} catch (std::exception& e) {
}
catch(std::exception& e)
{
qWarning() << QString("an error saving account and login occurred, msg: %1").arg(e.what());
} catch (...) {
}
catch(...)
{
qWarning() << "an error saving account and login occurred";
}
return false;
@ -167,24 +167,24 @@ RpcResponse<QJsonArray> openAccounts(const char* dataDirPath)
try
{
auto result = QString(OpenAccounts(const_cast<char*>(dataDirPath)));
if(result == "null")
return RpcResponse<QJsonArray>(QJsonArray());
if(result == "null") return RpcResponse<QJsonArray>(QJsonArray());
QJsonArray jsonResult;
if(!Utils::checkReceivedResponse(result, jsonResult)) {
if(!Utils::checkReceivedResponse(result, jsonResult))
{
throw std::domain_error("parsing response failed");
}
return Utils::buildPrivateRPCResponse(jsonResult);
}
catch (std::exception& e)
catch(std::exception& e)
{
auto response = RpcResponse<QJsonArray>(QJsonArray());
// TODO: don't translate exception messages. Exceptions are for developers and should never reach users
response.error.message = QObject::tr("an error opening accounts occurred, msg: %1").arg(e.what());
return response;
}
catch (...)
catch(...)
{
auto response = RpcResponse<QJsonArray>(QJsonArray());
response.error.message = QObject::tr("an error opening accounts occurred");
@ -192,14 +192,13 @@ RpcResponse<QJsonArray> openAccounts(const char* dataDirPath)
}
}
RpcResponse<QJsonObject> login(const QString& name, const QString& keyUid, const HashedPassword& password,
const QString& thumbnail, const QString& large)
RpcResponse<QJsonObject> login(const QString& name,
const QString& keyUid,
const HashedPassword& password,
const QString& thumbnail,
const QString& large)
{
QJsonObject payload{
{"name", name},
{"key-uid", keyUid},
{"identityImage", QJsonValue()}
};
QJsonObject payload{{"name", name}, {"key-uid", keyUid}, {"identityImage", QJsonValue()}};
if(!thumbnail.isEmpty() && !large.isEmpty())
{
@ -219,13 +218,13 @@ RpcResponse<QJsonObject> login(const QString& name, const QString& keyUid, const
return Utils::buildPrivateRPCResponse(jsonResult);
}
catch (std::exception& e)
catch(std::exception& e)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error logining in account occurred, msg: %1").arg(e.what());
return response;
}
catch (...)
catch(...)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error logining in account occurred");
@ -233,8 +232,12 @@ RpcResponse<QJsonObject> login(const QString& name, const QString& keyUid, const
}
}
RpcResponse<QJsonObject> loginWithConfig(const QString& name, const QString& keyUid, const HashedPassword& password,
const QString& thumbnail, const QString& large, const QJsonObject& nodeConfig)
RpcResponse<QJsonObject> loginWithConfig(const QString& name,
const QString& keyUid,
const HashedPassword& password,
const QString& thumbnail,
const QString& large,
const QJsonObject& nodeConfig)
{
QJsonObject payload{
{"name", name},
@ -261,13 +264,13 @@ RpcResponse<QJsonObject> loginWithConfig(const QString& name, const QString& key
return Utils::buildPrivateRPCResponse(jsonResult);
}
catch (std::exception& e)
catch(std::exception& e)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error logining in account occurred, msg: %1").arg(e.what());
return response;
}
catch (...)
catch(...)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error logining in account occurred");
@ -291,13 +294,13 @@ RpcResponse<QJsonObject> logout()
rpcResponse.error = Utils::getRPCErrorInJson(jsonResult).value_or(RpcError());
return rpcResponse;
}
catch (std::exception& e)
catch(std::exception& e)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error logging out account occurred, msg: %1").arg(e.what());
return response;
}
catch (...)
catch(...)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error logging out account occurred");
@ -305,4 +308,4 @@ RpcResponse<QJsonObject> logout()
}
}
}
} // namespace Status::StatusGo::Accounts

View File

@ -7,25 +7,35 @@
namespace Status::StatusGo::Accounts
{
RpcResponse<QJsonArray> generateAddresses(const std::vector<Accounts::DerivationPath> &paths);
RpcResponse<QJsonArray> generateAddresses(const std::vector<Accounts::DerivationPath>& paths);
RpcResponse<QString> generateAlias(const QString& publicKey);
RpcResponse<QString> generateAlias(const QString& publicKey);
RpcResponse<QJsonObject> storeDerivedAccounts(const QString& accountId, const HashedPassword& password,
const std::vector<Accounts::DerivationPath>& paths);
RpcResponse<QJsonObject> storeDerivedAccounts(const QString& accountId,
const HashedPassword& password,
const std::vector<Accounts::DerivationPath>& paths);
RpcResponse<QJsonObject> storeAccount(const QString& id, const HashedPassword& password);
RpcResponse<QJsonObject> storeAccount(const QString& id, const HashedPassword& password);
bool saveAccountAndLogin(const StatusGo::HashedPassword& password, const QJsonObject& account,
const QJsonArray& subaccounts, const QJsonObject& settings,
const QJsonObject& nodeConfig);
bool saveAccountAndLogin(const StatusGo::HashedPassword& password,
const QJsonObject& account,
const QJsonArray& subaccounts,
const QJsonObject& settings,
const QJsonObject& nodeConfig);
/// opens database and returns accounts list.
RpcResponse<QJsonArray> openAccounts(const char* dataDirPath);
/// opens database and returns accounts list.
RpcResponse<QJsonArray> openAccounts(const char* dataDirPath);
RpcResponse<QJsonObject> login(const QString& name, const QString& keyUid, const HashedPassword& password,
const QString& thumbnail, const QString& large);
RpcResponse<QJsonObject> loginWithConfig(const QString& name, const QString& keyUid, const HashedPassword& password,
const QString& thumbnail, const QString& large, const QJsonObject& nodeConfig);
RpcResponse<QJsonObject> logout();
}
RpcResponse<QJsonObject> login(const QString& name,
const QString& keyUid,
const HashedPassword& password,
const QString& thumbnail,
const QString& large);
RpcResponse<QJsonObject> loginWithConfig(const QString& name,
const QString& keyUid,
const HashedPassword& password,
const QString& thumbnail,
const QString& large,
const QJsonObject& nodeConfig);
RpcResponse<QJsonObject> logout();
} // namespace Status::StatusGo::Accounts

View File

@ -1,7 +1,7 @@
#include "AccountsAPI.h"
#include "Utils.h"
#include "Metadata/api_response.h"
#include "Utils.h"
#include <libstatus.h>
@ -14,13 +14,10 @@ using json = nlohmann::json;
namespace Status::StatusGo::Accounts
{
Accounts::ChatOrWalletAccounts getAccounts() {
Accounts::ChatOrWalletAccounts getAccounts()
{
// or even nicer with a raw string literal
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "accounts_getAccounts"},
{"params", json::array()}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "accounts_getAccounts"}, {"params", json::array()}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
auto resultJson = json::parse(result);
@ -29,71 +26,63 @@ Accounts::ChatOrWalletAccounts getAccounts() {
return resultJson.get<CallPrivateRpcResponse>().result;
}
void generateAccountWithDerivedPath(const HashedPassword &password, const QString &name, const QColor &color, const QString &emoji,
const DerivationPath &path, const EOAddress &derivedFrom)
void generateAccountWithDerivedPath(const HashedPassword& password,
const QString& name,
const QColor& color,
const QString& emoji,
const DerivationPath& path,
const EOAddress& derivedFrom)
{
std::vector<json> params = {password, name, color, emoji, path, derivedFrom};
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "accounts_generateAccountWithDerivedPath"},
{"params", params}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "accounts_generateAccountWithDerivedPath"}, {"params", params}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
auto resultJson = json::parse(result);
checkPrivateRpcCallResultAndReportError(resultJson);
}
void addAccountWithMnemonicAndPath(const QString &mnemonic, const HashedPassword &password, const QString &name,
const QColor &color, const QString &emoji, const DerivationPath &path)
void addAccountWithMnemonicAndPath(const QString& mnemonic,
const HashedPassword& password,
const QString& name,
const QColor& color,
const QString& emoji,
const DerivationPath& path)
{
std::vector<json> params = {mnemonic, password, name, color, emoji, path};
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "accounts_addAccountWithMnemonicAndPath"},
{"params", params}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "accounts_addAccountWithMnemonicAndPath"}, {"params", params}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
auto resultJson = json::parse(result);
checkPrivateRpcCallResultAndReportError(resultJson);
}
void addAccountWatch(const EOAddress &address, const QString &name, const QColor &color, const QString &emoji)
void addAccountWatch(const EOAddress& address, const QString& name, const QColor& color, const QString& emoji)
{
std::vector<json> params = {address, name, color, emoji};
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "accounts_addAccountWatch"},
{"params", params}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "accounts_addAccountWatch"}, {"params", params}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
auto resultJson = json::parse(result);
checkPrivateRpcCallResultAndReportError(resultJson);
}
void deleteAccount(const EOAddress &address)
void deleteAccount(const EOAddress& address)
{
std::vector<json> params = {address};
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "accounts_deleteAccount"},
{"params", params}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "accounts_deleteAccount"}, {"params", params}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
auto resultJson = json::parse(result);
checkPrivateRpcCallResultAndReportError(resultJson);
}
void deleteMultiaccount(const QString &keyUID, const fs::path &keyStoreDir)
void deleteMultiaccount(const QString& keyUID, const fs::path& keyStoreDir)
{
// We know go bridge won't misbehave with the input arguments
auto result = DeleteMultiaccount(const_cast<char*>(keyUID.toStdString().c_str()), const_cast<char*>(keyStoreDir.string().c_str()));
auto result = DeleteMultiaccount(const_cast<char*>(keyUID.toStdString().c_str()),
const_cast<char*>(keyStoreDir.string().c_str()));
auto resultJson = json::parse(result);
checkApiError(resultJson);
}
}
} // namespace Status::StatusGo::Accounts

View File

@ -5,8 +5,8 @@
#include "Accounts/ChatOrWalletAccount.h"
#include "Accounts/accounts_types.h"
#include <vector>
#include <filesystem>
#include <vector>
namespace Accounts = Status::StatusGo::Accounts;
@ -28,33 +28,40 @@ Accounts::ChatOrWalletAccounts getAccounts();
/// \note the underlying status-go API, SaveAccounts@accounts.go, returns `nil` for \c CallPrivateRpcResponse.result
/// \see \c getAccounts
/// \throws \c CallPrivateRpcError
void generateAccountWithDerivedPath(const HashedPassword &password, const QString &name,
const QColor &color, const QString &emoji,
const DerivationPath &path, const Accounts::EOAddress &derivedFrom);
void generateAccountWithDerivedPath(const HashedPassword& password,
const QString& name,
const QColor& color,
const QString& emoji,
const DerivationPath& path,
const Accounts::EOAddress& derivedFrom);
/// \brief Add a new account from an existing mnemonic
/// \note the underlying status-go api, SaveAccounts@accounts.go, returns `nil` for \c CallPrivateRpcResponse.result
/// \see \c getAccounts
/// \throws \c CallPrivateRpcError
void addAccountWithMnemonicAndPath(const QString &mnemonic, const HashedPassword &password, const QString &name,
const QColor &color, const QString &emoji, const DerivationPath &path);
void addAccountWithMnemonicAndPath(const QString& mnemonic,
const HashedPassword& password,
const QString& name,
const QColor& color,
const QString& emoji,
const DerivationPath& path);
/// \brief Add a watch only account
/// \note the underlying status-go api, SaveAccounts@accounts.go, returns `nil` for \c CallPrivateRpcResponse.result
/// \see \c getAccounts
/// \throws \c CallPrivateRpcError
void addAccountWatch(const EOAddress &address, const QString &name, const QColor &color, const QString &emoji);
void addAccountWatch(const EOAddress& address, const QString& name, const QColor& color, const QString& emoji);
/// \brief Delete an existing account
/// \note the underlying status-go api, DeleteAccount@accounts.go, returns `os.Remove(keyFile)`
/// \see \c getAccounts
/// \throws \c CallPrivateRpcError
void deleteAccount(const EOAddress &address);
void deleteAccount(const EOAddress& address);
/// \brief Delete an existing account
/// \note the underlying status-go api, DeleteAccount@accounts.go, returns `os.Remove(keyFile)`
/// \see \c getAccounts
/// \throws \c CallPrivateRpcError
void deleteMultiaccount(const QString &keyUID, const fs::path &keyStoreDir);
void deleteMultiaccount(const QString& keyUID, const fs::path& keyStoreDir);
} // namespaces
} // namespace Status::StatusGo::Accounts

View File

@ -1,27 +1,30 @@
#include "ChatOrWalletAccount.h"
namespace Status::StatusGo::Accounts {
namespace Status::StatusGo::Accounts
{
void to_json(json& j, const ChatOrWalletAccount& d) {
j = {{"address", d.address},
{"chat", d.isChat},
{"clock", d.clock},
{"color", d.color},
{"emoji", d.emoji},
{"hidden", d.isHidden},
{"mixedcase-address", d.mixedcaseAddress},
{"name", d.name},
{"path", d.path},
{"public-key", d.publicKey},
{"removed", d.isRemoved},
{"wallet", d.isWallet},
void to_json(json& j, const ChatOrWalletAccount& d)
{
j = {
{"address", d.address},
{"chat", d.isChat},
{"clock", d.clock},
{"color", d.color},
{"emoji", d.emoji},
{"hidden", d.isHidden},
{"mixedcase-address", d.mixedcaseAddress},
{"name", d.name},
{"path", d.path},
{"public-key", d.publicKey},
{"removed", d.isRemoved},
{"wallet", d.isWallet},
};
if(d.derivedFrom != std::nullopt)
j["derived-from"] = d.derivedFrom.value();
if(d.derivedFrom != std::nullopt) j["derived-from"] = d.derivedFrom.value();
}
void from_json(const json& j, ChatOrWalletAccount& d) {
void from_json(const json& j, ChatOrWalletAccount& d)
{
j.at("address").get_to(d.address);
j.at("chat").get_to(d.isChat);
j.at("clock").get_to(d.clock);
@ -34,13 +37,11 @@ void from_json(const json& j, ChatOrWalletAccount& d) {
j.at("wallet").get_to(d.isWallet);
constexpr auto pathKey = "path";
if(j.contains(pathKey))
j.at(pathKey).get_to(d.path);
if(j.contains(pathKey)) j.at(pathKey).get_to(d.path);
constexpr auto publicKeyKey = "public-key";
if(j.contains(publicKeyKey))
j.at(publicKeyKey).get_to(d.publicKey);
if(j.contains(publicKeyKey)) j.at(publicKeyKey).get_to(d.publicKey);
if(d.isWallet && !j.at("derived-from").get<std::string>().empty())
d.derivedFrom = j.at("derived-from").get<std::optional<EOAddress>>();
}
}
} // namespace Status::StatusGo::Accounts

View File

@ -12,8 +12,8 @@
using json = nlohmann::json;
namespace Status::StatusGo::Accounts {
namespace Status::StatusGo::Accounts
{
/// \brief Unique wallet account entity
/// \note equivalent of status-go's accounts.Account@multiaccounts/accounts/database.go
@ -39,4 +39,4 @@ using ChatOrWalletAccounts = std::vector<ChatOrWalletAccount>;
void to_json(json& j, const ChatOrWalletAccount& d);
void from_json(const json& j, ChatOrWalletAccount& d);
}
} // namespace Status::StatusGo::Accounts

View File

@ -7,10 +7,11 @@
using json = nlohmann::json;
/// Defines phantom types for strong typing
namespace Status::StatusGo::Accounts {
namespace Status::StatusGo::Accounts
{
/// The 20 byte address of an Ethereum account prefixed with 0x
using EOAddress = Helpers::NamedType<QString, struct EOAddressTag>;
using DerivationPath = Helpers::NamedType<QString, struct DerivationPathTag>;
}
} // namespace Status::StatusGo::Accounts

View File

@ -1,7 +1,7 @@
#include "ChatAPI.h"
#include "Utils.h"
#include "Metadata/api_response.h"
#include "Utils.h"
#include <libstatus.h>
@ -13,11 +13,7 @@ using namespace Status::StatusGo;
Chats::AllChannelGroupsDto Chats::getChats()
{
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "chat_getChats"},
{"params", json::array()}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "chat_getChats"}, {"params", json::array()}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
const auto resultJson = json::parse(result);

View File

@ -4,6 +4,6 @@
namespace Status::StatusGo::Chats
{
/// \brief Retrieve all available channel groups
AllChannelGroupsDto getChats();
}
/// \brief Retrieve all available channel groups
AllChannelGroupsDto getChats();
} // namespace Status::StatusGo::Chats

View File

@ -1,105 +1,118 @@
#include "ChatDto.h"
#include <Helpers/conversions.h>
#include <Helpers/JsonMacros.h>
#include <Helpers/conversions.h>
using namespace Status::StatusGo;
void Chats::to_json(json& j, const Category& d) {
j = {{"id", d.id},
{"name", d.name},
{"position", d.position},
};
void Chats::to_json(json& j, const Category& d)
{
j = {
{"id", d.id},
{"name", d.name},
{"position", d.position},
};
}
void Chats::from_json(const json& j, Category& d) {
void Chats::from_json(const json& j, Category& d)
{
STATUS_READ_NLOHMAN_JSON_PROPERTY(name)
STATUS_READ_NLOHMAN_JSON_PROPERTY(position)
STATUS_READ_NLOHMAN_JSON_PROPERTY(id, "category_id", false)
if (!j.contains("category_id")){
if(!j.contains("category_id"))
{
STATUS_READ_NLOHMAN_JSON_PROPERTY(id, "id", false)
}
}
void Chats::to_json(json& j, const Permission& d) {
j = {{"access", d.access},
{"ens_only", d.ensOnly},
};
void Chats::to_json(json& j, const Permission& d)
{
j = {
{"access", d.access},
{"ens_only", d.ensOnly},
};
}
void Chats::from_json(const json& j, Permission& d) {
void Chats::from_json(const json& j, Permission& d)
{
STATUS_READ_NLOHMAN_JSON_PROPERTY(access, "access", false)
STATUS_READ_NLOHMAN_JSON_PROPERTY(ensOnly, "ens_only", false)
}
void Chats::to_json(json& j, const Images& d) {
j = {{"large", d.large},
{"thumbnail", d.thumbnail},
{"banner", d.banner},
};
void Chats::to_json(json& j, const Images& d)
{
j = {
{"large", d.large},
{"thumbnail", d.thumbnail},
{"banner", d.banner},
};
}
void Chats::from_json(const json& j, Images& d) {
void Chats::from_json(const json& j, Images& d)
{
constexpr auto large = "large";
if(j.contains(large))
j[large].at("uri").get_to(d.large);
if(j.contains(large)) j[large].at("uri").get_to(d.large);
constexpr auto thumbnail = "thumbnail";
if(j.contains(thumbnail))
j[thumbnail].at("uri").get_to(d.thumbnail);
if(j.contains(thumbnail)) j[thumbnail].at("uri").get_to(d.thumbnail);
constexpr auto banner = "banner";
if(j.contains(banner))
j[banner].at("uri").get_to(d.banner);
if(j.contains(banner)) j[banner].at("uri").get_to(d.banner);
}
void Chats::to_json(json& j, const ChatMember& d) {
j = {{"id", d.id},
{"admin", d.admin},
{"joined", d.joined},
{"roles", d.roles},
};
void Chats::to_json(json& j, const ChatMember& d)
{
j = {
{"id", d.id},
{"admin", d.admin},
{"joined", d.joined},
{"roles", d.roles},
};
}
void Chats::from_json(const json& j, ChatMember& d) {
void Chats::from_json(const json& j, ChatMember& d)
{
STATUS_READ_NLOHMAN_JSON_PROPERTY(id)
STATUS_READ_NLOHMAN_JSON_PROPERTY(joined)
STATUS_READ_NLOHMAN_JSON_PROPERTY(roles)
STATUS_READ_NLOHMAN_JSON_PROPERTY(admin, "admin", false)
}
void Chats::to_json(json& j, const ChatDto& d) {
j = {{"id", d.id},
{"name", d.name},
{"description", d.description},
{"color", d.color},
{"emoji", d.emoji},
{"active", d.active},
{"timestamp", d.timestamp},
{"lastClockValue", d.lastClockValue},
{"deletedAtClockValue", d.deletedAtClockValue},
{"readMessagesAtClockValue", d.readMessagesAtClockValue},
{"unviewedMessagesCount", d.unviewedMessagesCount},
{"unviewedMentionsCount", d.unviewedMentionsCount},
{"canPost", d.canPost},
{"alias", d.alias},
{"identicon", d.icon},
{"muted", d.muted},
{"position", d.position},
{"communityId", d.communityId},
{"profile", d.profile},
{"joined", d.joined},
{"syncedTo", d.syncedTo},
{"syncedFrom", d.syncedFrom},
{"highlight", d.highlight},
{"categoryId", d.categoryId},
{"permissions", d.permissions},
{"chatType", d.chatType},
{"members", d.members},
};
void Chats::to_json(json& j, const ChatDto& d)
{
j = {
{"id", d.id},
{"name", d.name},
{"description", d.description},
{"color", d.color},
{"emoji", d.emoji},
{"active", d.active},
{"timestamp", d.timestamp},
{"lastClockValue", d.lastClockValue},
{"deletedAtClockValue", d.deletedAtClockValue},
{"readMessagesAtClockValue", d.readMessagesAtClockValue},
{"unviewedMessagesCount", d.unviewedMessagesCount},
{"unviewedMentionsCount", d.unviewedMentionsCount},
{"canPost", d.canPost},
{"alias", d.alias},
{"identicon", d.icon},
{"muted", d.muted},
{"position", d.position},
{"communityId", d.communityId},
{"profile", d.profile},
{"joined", d.joined},
{"syncedTo", d.syncedTo},
{"syncedFrom", d.syncedFrom},
{"highlight", d.highlight},
{"categoryId", d.categoryId},
{"permissions", d.permissions},
{"chatType", d.chatType},
{"members", d.members},
};
}
void Chats::from_json(const json& j, ChatDto& d) {
void Chats::from_json(const json& j, ChatDto& d)
{
STATUS_READ_NLOHMAN_JSON_PROPERTY(id)
STATUS_READ_NLOHMAN_JSON_PROPERTY(name)
STATUS_READ_NLOHMAN_JSON_PROPERTY(description)
@ -127,25 +140,31 @@ void Chats::from_json(const json& j, ChatDto& d) {
STATUS_READ_NLOHMAN_JSON_PROPERTY(chatType)
STATUS_READ_NLOHMAN_JSON_PROPERTY(categoryId, "categoryId", false)
if (!j.contains("categoryId")) {
if(!j.contains("categoryId"))
{
// Communities have `categoryID` and chats have `categoryId`
// This should be fixed in status-go, but would be a breaking change
STATUS_READ_NLOHMAN_JSON_PROPERTY(categoryId, "categoryID", false)
}
// Add community ID if needed
if (!d.communityId.isEmpty() && !d.id.contains(d.communityId)) {
if(!d.communityId.isEmpty() && !d.id.contains(d.communityId))
{
d.id = d.communityId + d.id;
}
constexpr auto membersKey = "members";
if (j.contains(membersKey)) {
if (j[membersKey].is_array()) {
if(j.contains(membersKey))
{
if(j[membersKey].is_array())
{
j.at(membersKey).get_to(d.members);
}
else if (j[membersKey].is_object()) {
else if(j[membersKey].is_object())
{
auto obj = j[membersKey];
for (json::const_iterator it = obj.cbegin(); it != obj.cend(); ++it) {
for(json::const_iterator it = obj.cbegin(); it != obj.cend(); ++it)
{
ChatMember chatMember;
it.value().get_to(chatMember);
chatMember.id = it.key().c_str();
@ -155,27 +174,30 @@ void Chats::from_json(const json& j, ChatDto& d) {
}
}
void Chats::to_json(json& j, const ChannelGroupDto& d) {
j = {{"id", d.id},
{"admin", d.admin},
{"verified", d.verified},
{"name", d.name},
{"description", d.description},
{"introMessage", d.introMessage},
{"outroMessage", d.outroMessage},
{"canManageUsers", d.canManageUsers},
{"color", d.color},
{"muted", d.muted},
{"images", d.images},
{"permissions", d.permissions},
{"channelGroupType", d.channelGroupType},
{"chats", d.chats},
{"categories", d.categories},
{"members", d.members},
};
void Chats::to_json(json& j, const ChannelGroupDto& d)
{
j = {
{"id", d.id},
{"admin", d.admin},
{"verified", d.verified},
{"name", d.name},
{"description", d.description},
{"introMessage", d.introMessage},
{"outroMessage", d.outroMessage},
{"canManageUsers", d.canManageUsers},
{"color", d.color},
{"muted", d.muted},
{"images", d.images},
{"permissions", d.permissions},
{"channelGroupType", d.channelGroupType},
{"chats", d.chats},
{"categories", d.categories},
{"members", d.members},
};
}
void Chats::from_json(const json& j, ChannelGroupDto& d) {
void Chats::from_json(const json& j, ChannelGroupDto& d)
{
STATUS_READ_NLOHMAN_JSON_PROPERTY(admin)
STATUS_READ_NLOHMAN_JSON_PROPERTY(verified)
STATUS_READ_NLOHMAN_JSON_PROPERTY(name)
@ -189,13 +211,14 @@ void Chats::from_json(const json& j, ChannelGroupDto& d) {
STATUS_READ_NLOHMAN_JSON_PROPERTY(permissions, "permissions", false)
STATUS_READ_NLOHMAN_JSON_PROPERTY(channelGroupType)
if (d.channelGroupType.isEmpty())
d.channelGroupType = ChannelGroupTypeUnknown;
if(d.channelGroupType.isEmpty()) d.channelGroupType = ChannelGroupTypeUnknown;
constexpr auto chats = "chats";
if (j.contains(chats)) {
if(j.contains(chats))
{
auto obj = j[chats];
for (json::const_iterator it = obj.cbegin(); it != obj.cend(); ++it) {
for(json::const_iterator it = obj.cbegin(); it != obj.cend(); ++it)
{
ChatDto chatDto;
it.value().get_to(chatDto);
d.chats.emplace_back(std::move(chatDto));
@ -203,9 +226,11 @@ void Chats::from_json(const json& j, ChannelGroupDto& d) {
}
constexpr auto categories = "categories";
if (j.contains(categories)) {
if(j.contains(categories))
{
auto obj = j[categories];
for (json::const_iterator it = obj.cbegin(); it != obj.cend(); ++it) {
for(json::const_iterator it = obj.cbegin(); it != obj.cend(); ++it)
{
Category category;
it.value().get_to(category);
d.categories.emplace_back(std::move(category));
@ -213,10 +238,13 @@ void Chats::from_json(const json& j, ChannelGroupDto& d) {
}
constexpr auto membersKey = "members";
if (j.contains(membersKey)) {
if (j[membersKey].is_object()) {
if(j.contains(membersKey))
{
if(j[membersKey].is_object())
{
auto obj = j[membersKey];
for (json::const_iterator it = obj.cbegin(); it != obj.cend(); ++it) {
for(json::const_iterator it = obj.cbegin(); it != obj.cend(); ++it)
{
ChatMember chatMember;
it.value().get_to(chatMember);
chatMember.id = it.key().c_str();
@ -226,16 +254,18 @@ void Chats::from_json(const json& j, ChannelGroupDto& d) {
}
}
void Chats::to_json(json& j, const AllChannelGroupsDto& d) {
void Chats::to_json(json& j, const AllChannelGroupsDto& d)
{
j = {{"id", d.allChannelGroups}};
}
void Chats::from_json(const json& j, AllChannelGroupsDto& d) {
for (json::const_iterator it = j.cbegin(); it != j.cend(); ++it) {
void Chats::from_json(const json& j, AllChannelGroupsDto& d)
{
for(json::const_iterator it = j.cbegin(); it != j.cend(); ++it)
{
ChannelGroupDto channelGroupDto;
it.value().get_to(channelGroupDto);
channelGroupDto.id = it.key().c_str();
d.allChannelGroups.emplace_back(std::move(channelGroupDto));
}
}

View File

@ -8,125 +8,135 @@
using json = nlohmann::json;
namespace Status::StatusGo::Chats {
namespace Status::StatusGo::Chats
{
constexpr auto ChannelGroupTypeUnknown = "unknown";
constexpr auto ChannelGroupTypePersonal = "personal";
constexpr auto ChannelGroupTypeCommunity = "community";
constexpr auto ChannelGroupTypeUnknown = "unknown";
constexpr auto ChannelGroupTypePersonal = "personal";
constexpr auto ChannelGroupTypeCommunity = "community";
enum ChatType
{
Unknown = 0,
OneToOne = 1,
Public = 2,
PrivateGroupChat = 3,
Profile = 4,
CommunityChat = 6
};
enum ChatType
{
Unknown = 0,
OneToOne = 1,
Public = 2,
PrivateGroupChat = 3,
Profile = 4,
CommunityChat = 6
};
struct Category {
QString id;
QString name;
int position;
};
struct Category
{
QString id;
QString name;
int position;
};
struct Permission {
int access;
bool ensOnly;
};
struct Permission
{
int access;
bool ensOnly;
};
struct Images {
QString thumbnail;
QString large;
QString banner;
};
struct Images
{
QString thumbnail;
QString large;
QString banner;
};
struct ChatMember {
QString id;
bool admin;
bool joined;
std::vector<int> roles;
};
struct ChatMember
{
QString id;
bool admin;
bool joined;
std::vector<int> roles;
};
struct ChatDto {
QString id; // ID is the id of the chat, for public chats it is the name e.g. status,
// for one-to-one is the hex encoded public key and for group chats is a random
// uuid appended with the hex encoded pk of the creator of the chat
QString name;
QString description;
QColor color;
QString emoji;
bool active; // indicates whether the chat has been soft deleted
ChatType chatType;
quint64 timestamp; // indicates the last time this chat has received/sent a message
quint64 lastClockValue; // indicates the last clock value to be used when sending messages
quint64 deletedAtClockValue; // indicates the clock value at time of deletion, messages with lower clock value of this should be discarded
quint64 readMessagesAtClockValue;
int unviewedMessagesCount;
int unviewedMentionsCount;
std::vector<ChatMember> members;
QString alias;
QString icon;
bool muted;
QString communityId; // set if chat belongs to a community
QString profile;
quint64 joined; // indicates when the user joined the chat last time
quint64 syncedTo;
quint64 syncedFrom;
bool canPost;
int position;
QString categoryId;
bool highlight;
Permission permissions;
};
struct ChatDto
{
QString id; // ID is the id of the chat, for public chats it is the name e.g. status,
// for one-to-one is the hex encoded public key and for group chats is a random
// uuid appended with the hex encoded pk of the creator of the chat
QString name;
QString description;
QColor color;
QString emoji;
bool active; // indicates whether the chat has been soft deleted
ChatType chatType;
quint64 timestamp; // indicates the last time this chat has received/sent a message
quint64 lastClockValue; // indicates the last clock value to be used when sending messages
quint64
deletedAtClockValue; // indicates the clock value at time of deletion, messages with lower clock value of this should be discarded
quint64 readMessagesAtClockValue;
int unviewedMessagesCount;
int unviewedMentionsCount;
std::vector<ChatMember> members;
QString alias;
QString icon;
bool muted;
QString communityId; // set if chat belongs to a community
QString profile;
quint64 joined; // indicates when the user joined the chat last time
quint64 syncedTo;
quint64 syncedFrom;
bool canPost;
int position;
QString categoryId;
bool highlight;
Permission permissions;
};
struct ChannelGroupDto {
QString id;
QString channelGroupType;
bool admin;
bool verified;
QString name;
QString ensName;
QString description;
QString introMessage;
QString outroMessage;
std::vector<ChatDto> chats;
std::vector<Category> categories;
Images images;
Permission permissions;
std::vector<ChatMember> members;
bool canManageUsers;
QColor color;
bool muted;
bool historyArchiveSupportEnabled;
bool pinMessageAllMembersEnabled;
};
struct ChannelGroupDto
{
QString id;
QString channelGroupType;
bool admin;
bool verified;
QString name;
QString ensName;
QString description;
QString introMessage;
QString outroMessage;
std::vector<ChatDto> chats;
std::vector<Category> categories;
Images images;
Permission permissions;
std::vector<ChatMember> members;
bool canManageUsers;
QColor color;
bool muted;
bool historyArchiveSupportEnabled;
bool pinMessageAllMembersEnabled;
};
struct AllChannelGroupsDto {
std::vector<ChannelGroupDto> allChannelGroups;
};
struct AllChannelGroupsDto
{
std::vector<ChannelGroupDto> allChannelGroups;
};
NLOHMANN_JSON_SERIALIZE_ENUM(ChatType, {
{Unknown, "Unknown"},
{OneToOne, "OneToOne"},
{Public, "Public"},
{PrivateGroupChat, "PrivateGroupChat"},
{Profile, "Profile"},
{CommunityChat, "CommunityChat"},
})
NLOHMANN_JSON_SERIALIZE_ENUM(ChatType,
{
{Unknown, "Unknown"},
{OneToOne, "OneToOne"},
{Public, "Public"},
{PrivateGroupChat, "PrivateGroupChat"},
{Profile, "Profile"},
{CommunityChat, "CommunityChat"},
})
void to_json(json& j, const Category& d);
void from_json(const json& j, Category& d);
void to_json(json& j, const Permission& d);
void from_json(const json& j, Permission& d);
void to_json(json& j, const Images& d);
void from_json(const json& j, Images& d);
void to_json(json& j, const ChatMember& d);
void from_json(const json& j, ChatMember& d);
void to_json(json& j, const ChatDto& d);
void from_json(const json& j, ChatDto& d);
void to_json(json& j, const ChannelGroupDto& d);
void from_json(const json& j, ChannelGroupDto& d);
void to_json(json& j, const AllChannelGroupsDto& d);
void from_json(const json& j, AllChannelGroupsDto& d);
}
void to_json(json& j, const Category& d);
void from_json(const json& j, Category& d);
void to_json(json& j, const Permission& d);
void from_json(const json& j, Permission& d);
void to_json(json& j, const Images& d);
void from_json(const json& j, Images& d);
void to_json(json& j, const ChatMember& d);
void from_json(const json& j, ChatMember& d);
void to_json(json& j, const ChatDto& d);
void from_json(const json& j, ChatDto& d);
void to_json(json& j, const ChannelGroupDto& d);
void from_json(const json& j, ChannelGroupDto& d);
void to_json(json& j, const AllChannelGroupsDto& d);
void from_json(const json& j, AllChannelGroupsDto& d);
} // namespace Status::StatusGo::Chats

View File

@ -13,13 +13,14 @@ RpcResponse<QJsonObject> initKeystore(const char* keystoreDir)
{
auto result = InitKeystore(const_cast<char*>(keystoreDir));
QJsonObject jsonResult;
if(!Utils::checkReceivedResponse(result, jsonResult)) {
if(!Utils::checkReceivedResponse(result, jsonResult))
{
throw std::domain_error("parsing response failed");
}
return Utils::buildPrivateRPCResponse(jsonResult);
}
catch (std::exception& e)
catch(std::exception& e)
{
// TODO: either use optional/smartpointers or exceptions instead of plain objects
auto response = RpcResponse<QJsonObject>(QJsonObject());
@ -27,7 +28,7 @@ RpcResponse<QJsonObject> initKeystore(const char* keystoreDir)
response.error.message = QObject::tr("an error opening accounts occurred, msg: %1").arg(e.what());
return response;
}
catch (...)
catch(...)
{
auto response = RpcResponse<QJsonObject>(QJsonObject());
response.error.message = QObject::tr("an error opening accounts occurred");
@ -35,4 +36,4 @@ RpcResponse<QJsonObject> initKeystore(const char* keystoreDir)
}
}
}
} // namespace Status::StatusGo::General

View File

@ -7,11 +7,7 @@ namespace Status::StatusGo::Messenger
bool startMessenger()
{
QJsonObject payload{
{"jsonrpc", "2.0"},
{"method", "wakuext_startMessenger"},
{"params", QJsonArray()}
};
QJsonObject payload{{"jsonrpc", "2.0"}, {"method", "wakuext_startMessenger"}, {"params", QJsonArray()}};
auto callResult = Utils::callPrivateRpc<QJsonObject>(Utils::jsonToByteArray(payload));
if(callResult.containsError())
@ -19,4 +15,4 @@ bool startMessenger()
return !callResult.containsError();
}
}
} // namespace Status::StatusGo::Messenger

View File

@ -1,25 +1,28 @@
#include "api_response.h"
namespace Status::StatusGo {
namespace Status::StatusGo
{
void checkApiError(const json &response) {
if(response.contains("error")) {
const auto &error = response["error"];
if(error.is_object()) {
void checkApiError(const json& response)
{
if(response.contains("error"))
{
const auto& error = response["error"];
if(error.is_object())
{
const auto apiErr = response["error"].get<ApiErrorResponseWithCode>();
throw CallGenericPrepareJsonError(apiErr);
}
assert(error.is_string());
const auto apiError = response.get<ApiErrorResponse>();
if(!apiError.error.empty())
throw CallGenericMakeJsonError(response.get<ApiErrorResponse>());
if(!apiError.error.empty()) throw CallGenericMakeJsonError(response.get<ApiErrorResponse>());
}
}
/// \throws \c CallPrivateRpcError, \c nlohmann::exception
void checkPrivateRpcCallResultAndReportError(const json &response) {
if(response.contains("error"))
throw CallPrivateRpcError(response.get<CallPrivateRpcErrorResponse>());
void checkPrivateRpcCallResultAndReportError(const json& response)
{
if(response.contains("error")) throw CallPrivateRpcError(response.get<CallPrivateRpcErrorResponse>());
}
} // namespace
} // namespace Status::StatusGo

View File

@ -8,7 +8,8 @@
using json = nlohmann::json;
namespace Status::StatusGo {
namespace Status::StatusGo
{
/*!
* \brief General API response if an internal status-go error occured
@ -18,7 +19,8 @@ namespace Status::StatusGo {
*
* \note update NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE when changing structure's content
*/
struct ApiErrorResponse {
struct ApiErrorResponse
{
std::string error;
};
@ -31,7 +33,8 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ApiErrorResponse, error)
*
* \note update NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE when changing structure's content
*/
struct JsonError {
struct JsonError
{
int code{0};
std::string message;
};
@ -45,7 +48,8 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(JsonError, code, message)
* \see jsonrpcSuccessfulResponse@response.go
* \note update NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE when changing structure's content
*/
struct ApiErrorResponseWithCode {
struct ApiErrorResponseWithCode
{
JsonError error;
};
@ -60,7 +64,8 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ApiErrorResponseWithCode, error)
*
* \note update NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE when changing structure's content
*/
struct ApiResponse {
struct ApiResponse
{
json result;
};
@ -116,38 +121,53 @@ void checkApiError(const json& response);
constexpr int defaultErrorCode = -32000;
class CallGenericMakeJsonError: public std::runtime_error {
class CallGenericMakeJsonError : public std::runtime_error
{
public:
CallGenericMakeJsonError(const ApiErrorResponse error)
: std::runtime_error("CallGenericMakeJsonError@status-go failed")
, m_error(std::move(error))
{}
{ }
const ApiErrorResponse& errorResponse() const
{
return m_error;
};
const ApiErrorResponse &errorResponse() const { return m_error; };
private:
const ApiErrorResponse m_error;
};
class CallGenericPrepareJsonError: public std::runtime_error {
class CallGenericPrepareJsonError : public std::runtime_error
{
public:
CallGenericPrepareJsonError(const ApiErrorResponseWithCode error)
: std::runtime_error("CallGenericPrepareJsonError@status-go failed")
, m_error(std::move(error))
{}
{ }
const ApiErrorResponseWithCode& errorResponse() const
{
return m_error;
};
const ApiErrorResponseWithCode &errorResponse() const { return m_error; };
private:
const ApiErrorResponseWithCode m_error;
};
class CallPrivateRpcError: public std::runtime_error {
class CallPrivateRpcError : public std::runtime_error
{
public:
CallPrivateRpcError(const CallPrivateRpcErrorResponse error)
: std::runtime_error("CallPrivateRPC@status-go failed")
, m_error(std::move(error))
{}
{ }
const CallPrivateRpcErrorResponse& errorResponse() const
{
return m_error;
};
const CallPrivateRpcErrorResponse &errorResponse() const { return m_error; };
private:
const CallPrivateRpcErrorResponse m_error;
};
@ -160,4 +180,4 @@ private:
*/
void checkPrivateRpcCallResultAndReportError(const json& response);
}
} // namespace Status::StatusGo

View File

@ -1,7 +1,7 @@
#include "SettingsAPI.h"
#include "Utils.h"
#include "Metadata/api_response.h"
#include "Utils.h"
#include <libstatus.h>
@ -13,11 +13,7 @@ using namespace Status::StatusGo;
Settings::SettingsDto Settings::getSettings()
{
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "settings_getSettings"},
{"params", json::array()}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "settings_getSettings"}, {"params", json::array()}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
const auto resultJson = json::parse(result);

View File

@ -4,6 +4,6 @@
namespace Status::StatusGo::Settings
{
/// \brief Retrieve settings
SettingsDto getSettings();
}
/// \brief Retrieve settings
SettingsDto getSettings();
} // namespace Status::StatusGo::Settings

View File

@ -1,20 +1,23 @@
#include "SettingsDto.h"
#include <Helpers/conversions.h>
#include <Helpers/JsonMacros.h>
#include <Helpers/conversions.h>
using namespace Status::StatusGo;
void Settings::to_json(json& j, const SettingsDto& d) {
j = {{"address", d.address},
{"display-name", d.displayName},
{"preferred-name", d.preferredName},
{"key-uid", d.keyUid},
{"public-key", d.publicKey},
};
void Settings::to_json(json& j, const SettingsDto& d)
{
j = {
{"address", d.address},
{"display-name", d.displayName},
{"preferred-name", d.preferredName},
{"key-uid", d.keyUid},
{"public-key", d.publicKey},
};
}
void Settings::from_json(const json& j, SettingsDto& d) {
void Settings::from_json(const json& j, SettingsDto& d)
{
STATUS_READ_NLOHMAN_JSON_PROPERTY(address)
STATUS_READ_NLOHMAN_JSON_PROPERTY(displayName, "display-name")
STATUS_READ_NLOHMAN_JSON_PROPERTY(preferredName, "preferred-name", false)

View File

@ -8,16 +8,18 @@
using json = nlohmann::json;
namespace Status::StatusGo::Settings {
namespace Status::StatusGo::Settings
{
struct SettingsDto {
QString address;
QString displayName;
QString preferredName;
QString keyUid;
QString publicKey;
};
struct SettingsDto
{
QString address;
QString displayName;
QString preferredName;
QString keyUid;
QString publicKey;
};
void to_json(json& j, const SettingsDto& d);
void from_json(const json& j, SettingsDto& d);
}
void to_json(json& j, const SettingsDto& d);
void from_json(const json& j, SettingsDto& d);
} // namespace Status::StatusGo::Settings

View File

@ -6,7 +6,8 @@
using namespace std::string_literals;
namespace Status::StatusGo {
namespace Status::StatusGo
{
std::map<std::string, SignalType> SignalsManager::signalMap;
@ -22,29 +23,25 @@ SignalsManager::SignalsManager()
{
SetSignalEventCallback((void*)&SignalsManager::signalCallback);
signalMap = {
{"node.ready"s, SignalType::NodeReady},
{"node.started"s, SignalType::NodeStarted},
{"node.stopped"s, SignalType::NodeStopped},
{"node.login"s, SignalType::NodeLogin},
{"node.crashed"s, SignalType::NodeCrashed},
signalMap = {{"node.ready"s, SignalType::NodeReady},
{"node.started"s, SignalType::NodeStarted},
{"node.stopped"s, SignalType::NodeStopped},
{"node.login"s, SignalType::NodeLogin},
{"node.crashed"s, SignalType::NodeCrashed},
{"discovery.started"s, SignalType::DiscoveryStarted},
{"discovery.stopped"s, SignalType::DiscoveryStopped},
{"discovery.summary"s, SignalType::DiscoverySummary},
{"discovery.started"s, SignalType::DiscoveryStarted},
{"discovery.stopped"s, SignalType::DiscoveryStopped},
{"discovery.summary"s, SignalType::DiscoverySummary},
{"mailserver.changed"s, SignalType::MailserverChanged},
{"mailserver.available"s, SignalType::MailserverAvailable},
{"mailserver.changed"s, SignalType::MailserverChanged},
{"mailserver.available"s, SignalType::MailserverAvailable},
{"history.request.started"s, SignalType::HistoryRequestStarted},
{"history.request.batch.processed"s, SignalType::HistoryRequestBatchProcessed},
{"history.request.completed"s, SignalType::HistoryRequestCompleted}
};
{"history.request.started"s, SignalType::HistoryRequestStarted},
{"history.request.batch.processed"s, SignalType::HistoryRequestBatchProcessed},
{"history.request.completed"s, SignalType::HistoryRequestCompleted}};
}
SignalsManager::~SignalsManager()
{
}
SignalsManager::~SignalsManager() { }
void SignalsManager::processSignal(const QString& statusSignal)
{
@ -82,46 +79,22 @@ void SignalsManager::decode(const QJsonObject& signalEvent)
switch(signalType)
{
// TODO: create extractor functions like in nim
case NodeLogin:
emit nodeLogin(signalError);
break;
case NodeReady:
emit nodeReady(signalError);
break;
case NodeStarted:
emit nodeStarted(signalError);
break;
case NodeStopped:
emit nodeStopped(signalError);
break;
case NodeLogin: emit nodeLogin(signalError); break;
case NodeReady: emit nodeReady(signalError); break;
case NodeStarted: emit nodeStarted(signalError); break;
case NodeStopped: emit nodeStopped(signalError); break;
case NodeCrashed:
qWarning() << "node.crashed, error: " << signalError;
emit nodeCrashed(signalError);
break;
case DiscoveryStarted:
emit discoveryStarted(signalError);
break;
case DiscoveryStopped:
emit discoveryStopped(signalError);
break;
case DiscoverySummary:
emit discoverySummary(signalEvent["event"].toArray().count(), signalError);
break;
case MailserverChanged:
emit mailserverChanged(signalError);
break;
case MailserverAvailable:
emit mailserverAvailable(signalError);
break;
case HistoryRequestStarted:
emit historyRequestStarted(signalError);
break;
case HistoryRequestBatchProcessed:
emit historyRequestBatchProcessed(signalError);
break;
case HistoryRequestCompleted:
emit historyRequestCompleted(signalError);
break;
case DiscoveryStarted: emit discoveryStarted(signalError); break;
case DiscoveryStopped: emit discoveryStopped(signalError); break;
case DiscoverySummary: emit discoverySummary(signalEvent["event"].toArray().count(), signalError); break;
case MailserverChanged: emit mailserverChanged(signalError); break;
case MailserverAvailable: emit mailserverAvailable(signalError); break;
case HistoryRequestStarted: emit historyRequestStarted(signalError); break;
case HistoryRequestBatchProcessed: emit historyRequestBatchProcessed(signalError); break;
case HistoryRequestCompleted: emit historyRequestCompleted(signalError); break;
case Unknown: assert(false); break;
}
}
@ -130,9 +103,8 @@ void SignalsManager::signalCallback(const char* data)
{
// TODO: overkill, use some kind of message broker
auto dataStrPtr = std::make_shared<QString>(data);
QFuture<void> future = QtConcurrent::run([dataStrPtr](){
SignalsManager::instance()->processSignal(*dataStrPtr);
});
QFuture<void> future =
QtConcurrent::run([dataStrPtr]() { SignalsManager::instance()->processSignal(*dataStrPtr); });
}
}
} // namespace Status::StatusGo

View File

@ -2,7 +2,8 @@
#include <QObject>
namespace Status::StatusGo {
namespace Status::StatusGo
{
enum SignalType
{
@ -35,7 +36,6 @@ class SignalsManager final : public QObject
Q_OBJECT
public:
static SignalsManager* instance();
void processSignal(const QString& ev);
@ -57,6 +57,7 @@ signals:
void historyRequestStarted(const QString& error);
void historyRequestBatchProcessed(const QString& error);
void historyRequestCompleted(const QString& error);
private:
explicit SignalsManager();
~SignalsManager();
@ -67,4 +68,4 @@ private:
void decode(const QJsonObject& signalEvent);
};
}
} // namespace Status::StatusGo

View File

@ -30,18 +30,20 @@ struct RpcResponse
int id;
RpcError error;
explicit
RpcResponse(T result, QString version = RpcError::UnknownVersion, int id = RpcError::UnknownId,
RpcError error = RpcError())
explicit RpcResponse(T result,
QString version = RpcError::UnknownVersion,
int id = RpcError::UnknownId,
RpcError error = RpcError())
: result(std::move(result))
, jsonRpcVersion(std::move(version))
, id(id)
, error(std::move(error))
{ }
bool containsError() const {
bool containsError() const
{
return !error.message.isEmpty() || error.code != RpcError::NoError;
}
};
}
} // namespace Status::StatusGo

View File

@ -15,35 +15,35 @@ QJsonArray toJsonArray(const std::vector<Accounts::DerivationPath>& value)
return array;
}
const char* statusGoCallPrivateRPC(const char* inputJSON) {
const char* statusGoCallPrivateRPC(const char* inputJSON)
{
// Evil done here! status-go API doesn't follow the proper const conventions
return CallPrivateRPC(const_cast<char*>(inputJSON));
}
HashedPassword hashPassword(const QString &str)
HashedPassword hashPassword(const QString& str)
{
// TODO: is utf8 the standard used by NIM also? Will it unlock DBs encrypted with NIM password hashing?
return HashedPassword("0x" + QString::fromUtf8(QCryptographicHash::hash(str.toUtf8(),
QCryptographicHash::Keccak_256).toHex().toUpper()));
return HashedPassword(
"0x" +
QString::fromUtf8(QCryptographicHash::hash(str.toUtf8(), QCryptographicHash::Keccak_256).toHex().toUpper()));
}
std::optional<RpcError> getRPCErrorInJson(const QJsonObject& json)
{
auto errVal = json[Param::Error];
if (errVal.isNull() || errVal.isUndefined())
return std::nullopt;
if(errVal.isString() && errVal.toString().length() == 0)
return std::nullopt;
if(errVal.isNull() || errVal.isUndefined()) return std::nullopt;
if(errVal.isString() && errVal.toString().length() == 0) return std::nullopt;
RpcError response;
auto errObj = json[Param::Id].toObject();
if (!errObj[Param::ErrorCode].isNull() && !errObj[Param::ErrorCode].isUndefined())
if(!errObj[Param::ErrorCode].isNull() && !errObj[Param::ErrorCode].isUndefined())
response.code = errObj[Param::ErrorCode].toInt();
if (!errObj[Param::ErrorMessage].isNull() && !errObj[Param::ErrorMessage].isUndefined())
if(!errObj[Param::ErrorMessage].isNull() && !errObj[Param::ErrorMessage].isUndefined())
response.message = errObj[Param::ErrorMessage].toString();
else
response.message = errVal.toString();
return response;
}
}
} // namespace Status::StatusGo::Utils

View File

@ -1,29 +1,30 @@
#pragma once
#include "Types.h"
#include "Accounts/accounts_types.h"
#include "Types.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
namespace Status::StatusGo::Utils
{
namespace Param {
static constexpr auto Id{"id"};
static constexpr auto JsonRpc{"jsonrpc"};
static constexpr auto Result{"result"};
static constexpr auto Error{"error"};
static constexpr auto ErrorMessage{"message"};
static constexpr auto ErrorCode{"code"};
}
namespace Param
{
static constexpr auto Id{"id"};
static constexpr auto JsonRpc{"jsonrpc"};
static constexpr auto Result{"result"};
static constexpr auto Error{"error"};
static constexpr auto ErrorMessage{"message"};
static constexpr auto ErrorCode{"code"};
} // namespace Param
template<class T>
template <class T>
QByteArray jsonToByteArray(const T& json)
{
static_assert(std::is_same_v<T, QJsonObject> ||
std::is_same_v<T, QJsonArray>, "Wrong Json type. Supported: Object, Array");
static_assert(std::is_same_v<T, QJsonObject> || std::is_same_v<T, QJsonArray>,
"Wrong Json type. Supported: Object, Array");
return QJsonDocument(json).toJson(QJsonDocument::Compact);
}
@ -32,20 +33,19 @@ QJsonArray toJsonArray(const std::vector<Accounts::DerivationPath>& value);
/// Check if json contains a standard status-go error and
std::optional<RpcError> getRPCErrorInJson(const QJsonObject& json);
template<class T>
template <class T>
bool checkReceivedResponse(const QString& response, T& json)
{
QJsonParseError error;
auto jsonDocument = QJsonDocument::fromJson(response.toUtf8(), &error);
if (error.error != QJsonParseError::NoError)
return false;
if(error.error != QJsonParseError::NoError) return false;
if constexpr (std::is_same_v<T, QJsonObject>)
if constexpr(std::is_same_v<T, QJsonObject>)
{
json = jsonDocument.object();
return true;
}
else if constexpr (std::is_same_v<T, QJsonArray>)
else if constexpr(std::is_same_v<T, QJsonArray>)
{
json = jsonDocument.array();
return true;
@ -55,25 +55,24 @@ bool checkReceivedResponse(const QString& response, T& json)
}
// TODO: Clarify scope. The assumption done here are valid only for status-go::CallPrivateRPC API.
template<class T>
template <class T>
RpcResponse<T> buildPrivateRPCResponse(const T& json)
{
auto response = RpcResponse<T>(T());
if constexpr (std::is_same_v<T, QJsonObject>)
if constexpr(std::is_same_v<T, QJsonObject>)
{
if (!json[Param::Id].isNull() && !json[Param::Id].isUndefined())
response.id = json[Param::Id].toInt();
if(!json[Param::Id].isNull() && !json[Param::Id].isUndefined()) response.id = json[Param::Id].toInt();
if (!json[Param::JsonRpc].isNull() && !json[Param::JsonRpc].isUndefined())
response.jsonRpcVersion = json[Param::JsonRpc].toString();
if(!json[Param::JsonRpc].isNull() && !json[Param::JsonRpc].isUndefined())
response.jsonRpcVersion = json[Param::JsonRpc].toString();
response.error = getRPCErrorInJson(json).value_or(RpcError());
response.error = getRPCErrorInJson(json).value_or(RpcError());
if (!json[Param::Result].isNull() && !json[Param::Result].isUndefined())
response.result = json[Param::Result].toObject();
if(!json[Param::Result].isNull() && !json[Param::Result].isUndefined())
response.result = json[Param::Result].toObject();
}
else if constexpr (std::is_same_v<T, QJsonArray>)
else if constexpr(std::is_same_v<T, QJsonArray>)
{
response.result = json;
}
@ -83,7 +82,7 @@ RpcResponse<T> buildPrivateRPCResponse(const T& json)
const char* statusGoCallPrivateRPC(const char* inputJSON);
template<class T>
template <class T>
RpcResponse<T> callPrivateRpc(const QByteArray& payload)
{
try
@ -98,13 +97,13 @@ RpcResponse<T> callPrivateRpc(const QByteArray& payload)
return Utils::buildPrivateRPCResponse(jsonResult);
}
catch (std::exception& e)
catch(std::exception& e)
{
auto response = RpcResponse<T>(T());
response.error.message = QObject::tr("an error executing rpc call occurred, msg: %1").arg(e.what());
return response;
}
catch (...)
catch(...)
{
auto response = RpcResponse<T>(T());
response.error.message = QObject::tr("an error executing rpc call");
@ -112,6 +111,6 @@ RpcResponse<T> callPrivateRpc(const QByteArray& payload)
}
}
HashedPassword hashPassword(const QString &str);
HashedPassword hashPassword(const QString& str);
}
} // namespace Status::StatusGo::Utils

View File

@ -1,42 +1,42 @@
#include "BigInt.h"
#include <locale>
#include <iostream>
#include <locale>
#include <Helpers/helpers.h>
namespace Status {
namespace StatusGo::Wallet {
std::string toHexData(const BigInt &num, bool uppercase)
namespace Status
{
namespace StatusGo::Wallet
{
return num.str(0, std::ios_base::showbase | std::ios_base::hex | (uppercase ? std::ios_base::uppercase : std::ios_base::fmtflags(0)));
}
std::string toHexData(const BigInt& num, bool uppercase)
{
return num.str(0,
std::ios_base::showbase | std::ios_base::hex |
(uppercase ? std::ios_base::uppercase : std::ios_base::fmtflags(0)));
}
using namespace QtLiterals;
bool has0xPrefix(const QByteArray &in) {
bool has0xPrefix(const QByteArray& in)
{
return in.size() >= 2 && Helpers::iequals(in.first(2), "0x"_qba);
}
BigInt parseHex(const std::string &value)
BigInt parseHex(const std::string& value)
{
auto data = QByteArray::fromRawData(value.c_str(), value.size());
if (!has0xPrefix(data))
throw std::runtime_error("BigInt::parseHex missing 0x prefix");
if (data.size() == 2)
throw std::runtime_error("BigInt::parseHex empty number");
if (data.size() > 3 && data[2] == '0')
throw std::runtime_error("BigInt::parseHex leading zero");
if (data.size() > 66)
throw std::runtime_error("BigInt::parseHex more than 256 bits");
if(!has0xPrefix(data)) throw std::runtime_error("BigInt::parseHex missing 0x prefix");
if(data.size() == 2) throw std::runtime_error("BigInt::parseHex empty number");
if(data.size() > 3 && data[2] == '0') throw std::runtime_error("BigInt::parseHex leading zero");
if(data.size() > 66) throw std::runtime_error("BigInt::parseHex more than 256 bits");
return BigInt{data.data()};
}
} // namespace StatusGo::Wallet
QString toQString(const StatusGo::Wallet::BigInt &num)
QString toQString(const StatusGo::Wallet::BigInt& num)
{
return QString::fromStdString(num.str(0, std::ios_base::dec));
}

View File

@ -11,37 +11,43 @@
using json = nlohmann::json;
namespace Status {
namespace StatusGo::Wallet {
namespace Status
{
namespace StatusGo::Wallet
{
using BigInt = boost::multiprecision::uint256_t;
/// Converts into the 0x prefixed hexadecimal display required by status-go (also uppercase)
std::string toHexData(const BigInt &num, bool uppercase = false);
std::string toHexData(const BigInt& num, bool uppercase = false);
/// \throws std::runtime_error if value is not a valid status-go hex string
/// or value is higher than 2^64 (numbers larger than 256 bits are not accepted)
/// \see toHexData
BigInt parseHex(const std::string &value);
BigInt parseHex(const std::string& value);
}
} // namespace StatusGo::Wallet
/// Human readable form
QString toQString(const StatusGo::Wallet::BigInt &num);
QString toQString(const StatusGo::Wallet::BigInt& num);
} // Status::StatusGo::Wallet
} // namespace Status
namespace nlohmann {
namespace nlohmann
{
namespace GoWallet = Status::StatusGo::Wallet;
template<>
struct adl_serializer<GoWallet::BigInt> {
static void to_json(json& j, const GoWallet::BigInt& num) {
template <>
struct adl_serializer<GoWallet::BigInt>
{
static void to_json(json& j, const GoWallet::BigInt& num)
{
j = GoWallet::toHexData(num);
}
static void from_json(const json& j, GoWallet::BigInt& num) {
static void from_json(const json& j, GoWallet::BigInt& num)
{
num = GoWallet::BigInt(j.get<std::string>());
}
};

View File

@ -14,7 +14,8 @@ namespace Accounts = Status::StatusGo::Accounts;
using json = nlohmann::json;
namespace Status::StatusGo::Wallet {
namespace Status::StatusGo::Wallet
{
/// \brief Define a derived address as returned by the corresponding API
/// \note equivalent of status-go's DerivedAddress@api.go
@ -31,4 +32,4 @@ using DerivedAddresses = std::vector<DerivedAddress>;
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DerivedAddress, address, path, hasActivity, alreadyCreated);
}
} // namespace Status::StatusGo::Wallet

View File

@ -12,7 +12,8 @@
using json = nlohmann::json;
/// \note not sure if this is the best namespace, ok for now
namespace Status::StatusGo::Wallet {
namespace Status::StatusGo::Wallet
{
/// \note equivalent of status-go's Network@config.go (params package)
struct NetworkConfiguration
@ -34,9 +35,19 @@ struct NetworkConfiguration
using NetworkConfigurations = std::vector<NetworkConfiguration>;
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NetworkConfiguration, chainId, chainName, rpcUrl, blockExplorerUrl,
iconUrl, nativeCurrencyName, nativeCurrencySymbol,
nativeCurrencyDecimals, isTest, layer, enabled, chainColor,
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NetworkConfiguration,
chainId,
chainName,
rpcUrl,
blockExplorerUrl,
iconUrl,
nativeCurrencyName,
nativeCurrencySymbol,
nativeCurrencyDecimals,
isTest,
layer,
enabled,
chainColor,
shortName);
}
} // namespace Status::StatusGo::Wallet

View File

@ -1,7 +1,7 @@
#pragma once
#include "wallet_types.h"
#include "Accounts/accounts_types.h"
#include "wallet_types.h"
#include <Helpers/conversions.h>
@ -11,7 +11,8 @@
namespace Accounts = Status::StatusGo::Accounts;
namespace Status::StatusGo::Wallet {
namespace Status::StatusGo::Wallet
{
/// \brief Define a saved wallet address as returned by the corresponding API
/// \note equivalent of status-go's SavedAddress@api.go
@ -28,4 +29,4 @@ using SavedAddresses = std::vector<SavedAddress>;
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SavedAddress, address, name, favourite, chainId);
}
} // namespace Status::StatusGo::Wallet

View File

@ -14,24 +14,24 @@ namespace Accounts = Status::StatusGo::Accounts;
using json = nlohmann::json;
namespace Status::StatusGo::Wallet {
namespace Status::StatusGo::Wallet
{
/// \note equivalent of status-go's Token@token.go
/// \see \c getDerivedAddressesForPath
struct Token
{
Accounts::EOAddress address;
QString name;
QString symbol;
QColor color;
unsigned int decimals;
ChainID chainId;
QString name;
QString symbol;
QColor color;
unsigned int decimals;
ChainID chainId;
};
using TokenPtr = std::shared_ptr<Token>;
using Tokens = std::vector<Token>;
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Token, address, name,symbol,
color, decimals, chainId);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Token, address, name, symbol, color, decimals, chainId);
}
} // namespace Status::StatusGo::Wallet

View File

@ -1,7 +1,7 @@
#include "WalletApi.h"
#include "Utils.h"
#include "Metadata/api_response.h"
#include "Utils.h"
#include "Accounts/accounts_types.h"
@ -18,14 +18,14 @@ using json = nlohmann::json;
namespace Status::StatusGo::Wallet
{
DerivedAddresses getDerivedAddressesForPath(const HashedPassword &password, const Accounts::EOAddress &derivedFrom, const Accounts::DerivationPath &path, int pageSize, int pageNumber)
DerivedAddresses getDerivedAddressesForPath(const HashedPassword& password,
const Accounts::EOAddress& derivedFrom,
const Accounts::DerivationPath& path,
int pageSize,
int pageNumber)
{
std::vector<json> params = {password, derivedFrom, path, pageSize, pageNumber};
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "wallet_getDerivedAddressesForPath"},
{"params", params}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "wallet_getDerivedAddressesForPath"}, {"params", params}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
const auto resultJson = json::parse(result);
@ -36,27 +36,20 @@ DerivedAddresses getDerivedAddressesForPath(const HashedPassword &password, cons
SavedAddresses getSavedAddresses()
{
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "wallet_getSavedAddresses"}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "wallet_getSavedAddresses"}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
const auto resultJson = json::parse(result);
checkPrivateRpcCallResultAndReportError(resultJson);
const auto &data = resultJson.get<CallPrivateRpcResponse>().result;
const auto& data = resultJson.get<CallPrivateRpcResponse>().result;
return data.is_null() ? json::array() : data;
}
void saveAddress(const SavedAddress &address)
void saveAddress(const SavedAddress& address)
{
std::vector<json> params = { address };
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "wakuext_upsertSavedAddress"},
{"params", params}
};
std::vector<json> params = {address};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "wakuext_upsertSavedAddress"}, {"params", params}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
auto resultJson = json::parse(result);
@ -66,64 +59,53 @@ void saveAddress(const SavedAddress &address)
NetworkConfigurations getEthereumChains(bool onlyEnabled)
{
std::vector<json> params = {onlyEnabled};
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "wallet_getEthereumChains"},
{"params", params}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "wallet_getEthereumChains"}, {"params", params}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
const auto resultJson = json::parse(result);
checkPrivateRpcCallResultAndReportError(resultJson);
const auto &data = resultJson.get<CallPrivateRpcResponse>().result;
const auto& data = resultJson.get<CallPrivateRpcResponse>().result;
return data.is_null() ? json::array() : data;
}
Tokens getTokens(const ChainID &chainId)
Tokens getTokens(const ChainID& chainId)
{
std::vector<json> params = {chainId};
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "wallet_getTokens"},
{"params", params}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "wallet_getTokens"}, {"params", params}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
const auto resultJson = json::parse(result);
checkPrivateRpcCallResultAndReportError(resultJson);
const auto &data = resultJson.get<CallPrivateRpcResponse>().result;
const auto& data = resultJson.get<CallPrivateRpcResponse>().result;
return data.is_null() ? json::array() : data;
}
TokenBalances getTokensBalancesForChainIDs(const std::vector<ChainID> &chainIds,
TokenBalances getTokensBalancesForChainIDs(const std::vector<ChainID>& chainIds,
const std::vector<Accounts::EOAddress> accounts,
const std::vector<Accounts::EOAddress> tokens)
{
std::vector<json> params = {chainIds, accounts, tokens};
json inputJson = {
{"jsonrpc", "2.0"},
{"method", "wallet_getTokensBalancesForChainIDs"},
{"params", params}
};
json inputJson = {{"jsonrpc", "2.0"}, {"method", "wallet_getTokensBalancesForChainIDs"}, {"params", params}};
auto result = Utils::statusGoCallPrivateRPC(inputJson.dump().c_str());
const auto resultJson = json::parse(result);
checkPrivateRpcCallResultAndReportError(resultJson);
TokenBalances resultData;
const auto &data = resultJson.get<CallPrivateRpcResponse>().result;
const auto& data = resultJson.get<CallPrivateRpcResponse>().result;
// Workaround to exception "type must be array, but is object" for custom key-types
// TODO: find out why
std::map<std::string, std::map<std::string, BigInt>> dataMap = data.is_null() ? nlohmann::json() : data;
for(const auto &keyIt : dataMap) {
for(const auto& keyIt : dataMap)
{
std::map<Accounts::EOAddress, BigInt> val;
for(const auto &valIt : keyIt.second)
for(const auto& valIt : keyIt.second)
val.emplace(QString::fromStdString(valIt.first), valIt.second);
resultData.emplace(QString::fromStdString(keyIt.first), std::move(val));
}
return resultData;
}
} // namespaces
} // namespace Status::StatusGo::Wallet

View File

@ -6,8 +6,8 @@
#include "DerivedAddress.h"
#include "NetworkConfiguration.h"
#include "Token.h"
#include "SavedAddress.h"
#include "Token.h"
#include "Types.h"
@ -20,10 +20,11 @@ namespace Status::StatusGo::Wallet
/// \brief Retrieve a list of derived account addresses
/// \see \c generateAccountWithDerivedPath
/// \throws \c CallPrivateRpcError
DerivedAddresses getDerivedAddressesForPath(const HashedPassword &password,
const Accounts::EOAddress &derivedFrom,
const Accounts::DerivationPath &path,
int pageSize, int pageNumber);
DerivedAddresses getDerivedAddressesForPath(const HashedPassword& password,
const Accounts::EOAddress& derivedFrom,
const Accounts::DerivationPath& path,
int pageSize,
int pageNumber);
/// \brief Retrieve a list of saved wallet addresses
/// \see \c getSavedAddresses
@ -33,7 +34,7 @@ SavedAddresses getSavedAddresses();
/// \brief Add a new or update existing saved wallet address
/// \see wakuext_upsertSavedAddress RPC method
/// \throws \c CallPrivateRpcError
void saveAddress(const SavedAddress &address);
void saveAddress(const SavedAddress& address);
/// \note status-go's GetEthereumChains@api.go which calls
/// NetworkManager@client.go -> network.Manager.get()
@ -46,15 +47,14 @@ NetworkConfigurations getEthereumChains(bool onlyEnabled);
/// \throws \c CallPrivateRpcError for general RPC call failure
NetworkConfigurations getEthereumChains(bool onlyEnabled);
/// \note status-go's GetTokens@api.go -> TokenManager.getTokens@token.go
/// \throws \c CallPrivateRpcError with
Tokens getTokens(const ChainID &chainId);
Tokens getTokens(const ChainID& chainId);
using TokenBalances = std::map<Accounts::EOAddress, std::map<Accounts::EOAddress, BigInt>>;
/// \note status-go's @api.go -> <xx>@<xx>.go
/// \throws \c CallPrivateRpcError
TokenBalances getTokensBalancesForChainIDs(const std::vector<ChainID> &chainIds,
TokenBalances getTokensBalancesForChainIDs(const std::vector<ChainID>& chainIds,
const std::vector<Accounts::EOAddress> accounts,
const std::vector<Accounts::EOAddress> tokens);
} // namespaces
} // namespace Status::StatusGo::Wallet

View File

@ -3,13 +3,13 @@
#include <nlohmann/json.hpp>
#include <QString>
using json = nlohmann::json;
/// Defines phantom types for strong typing
namespace Status::StatusGo::Wallet {
namespace Status::StatusGo::Wallet
{
using ChainID = Helpers::NamedType<unsigned long long int, struct ChainIDTag>;

View File

@ -6,7 +6,8 @@
using json = nlohmann::json;
namespace StatusGo = Status::StatusGo;
namespace Status::Testing {
namespace Status::Testing
{
TEST(StatusGoQt, TestJsonParsing)
{
@ -19,13 +20,15 @@ TEST(StatusGoQt, TestJsonParsing)
ASSERT_EQ(callRawRPCJson.error.message, expectedJsonError.message);
auto callRawRPCBadJsonKeyStr = R"({"unknown":"2.0","id":42,"error":{"code":-32601,"message":"Method not found"}})";
ASSERT_THROW(json::parse(callRawRPCBadJsonKeyStr).get<StatusGo::CallPrivateRpcErrorResponse>(), nlohmann::detail::out_of_range);
ASSERT_THROW(json::parse(callRawRPCBadJsonKeyStr).get<StatusGo::CallPrivateRpcErrorResponse>(),
nlohmann::detail::out_of_range);
auto callRawRPCBadJsonValStr = R"({"jsonrpc":"2.0","id":42,"error":23})";
ASSERT_THROW(json::parse(callRawRPCBadJsonValStr).get<StatusGo::CallPrivateRpcErrorResponse>(), nlohmann::detail::type_error);
ASSERT_THROW(json::parse(callRawRPCBadJsonValStr).get<StatusGo::CallPrivateRpcErrorResponse>(),
nlohmann::detail::type_error);
auto statusGoWithResultJsonStr = R"({"result":"0x123"})";
auto statusGoWithResultJson = json::parse(statusGoWithResultJsonStr).get<StatusGo::ApiResponse>();
ASSERT_EQ(statusGoWithResultJson.result, "0x123");
}
} // namespace
} // namespace Status::Testing

View File

@ -4,7 +4,8 @@
namespace fs = std::filesystem;
namespace Status::Testing {
namespace Status::Testing
{
fs::path createTestFolder(const std::string& testName)
{
@ -12,14 +13,13 @@ fs::path createTestFolder(const std::string& testName)
auto tm = *std::localtime(&t);
std::ostringstream timeOss;
timeOss << std::put_time(&tm, "%d-%m-%Y_%H-%M-%S");
return fs::path(testing::TempDir())/"StatusTests"/(testName + "-" + timeOss.str());
return fs::path(testing::TempDir()) / "StatusTests" / (testName + "-" + timeOss.str());
}
AutoCleanTempTestDir::AutoCleanTempTestDir(const std::string &testName, bool createDir)
AutoCleanTempTestDir::AutoCleanTempTestDir(const std::string& testName, bool createDir)
: m_testFolder(createTestFolder(testName))
{
if(createDir)
fs::create_directories(m_testFolder);
if(createDir) fs::create_directories(m_testFolder);
}
AutoCleanTempTestDir::~AutoCleanTempTestDir()
@ -33,4 +33,4 @@ const std::filesystem::path& AutoCleanTempTestDir::tempFolder()
return m_testFolder;
}
}
} // namespace Status::Testing

View File

@ -4,9 +4,11 @@
#include <string>
namespace Status::Testing {
namespace Status::Testing
{
class AutoCleanTempTestDir {
class AutoCleanTempTestDir
{
public:
/// Creates a temporary folder to be used in tests. The folder content's will
/// be removed when out of scope
@ -19,4 +21,4 @@ private:
const std::filesystem::path m_testFolder;
};
}
} // namespace Status::Testing

View File

@ -3,13 +3,13 @@
#include <stdio.h>
#include <stdlib.h>
namespace Status::Testing {
namespace Status::Testing
{
std::weak_ptr<QString> MonitorQtOutput::m_qtMessageOutputForSharing;
std::mutex MonitorQtOutput::m_mutex;
QtMessageHandler MonitorQtOutput::m_previousHandler = nullptr;
MonitorQtOutput::MonitorQtOutput()
{
// Ensure only one instance registers a handler
@ -17,13 +17,14 @@ MonitorQtOutput::MonitorQtOutput()
std::unique_lock<std::mutex> localLock(m_mutex);
auto globalMsgOut = m_qtMessageOutputForSharing.lock();
auto prev = qInstallMessageHandler(qtMessageOutput);
if(prev != qtMessageOutput)
m_previousHandler = prev;
if(!globalMsgOut) {
if(prev != qtMessageOutput) m_previousHandler = prev;
if(!globalMsgOut)
{
m_thisMessageOutput = std::make_shared<QString>();
m_qtMessageOutputForSharing = m_thisMessageOutput;
}
else {
else
{
m_thisMessageOutput = globalMsgOut;
m_start = m_thisMessageOutput->length();
}
@ -32,15 +33,15 @@ MonitorQtOutput::MonitorQtOutput()
MonitorQtOutput::~MonitorQtOutput()
{
std::unique_lock<std::mutex> localLock(m_mutex);
if(m_thisMessageOutput.use_count() == 1) {
if(m_thisMessageOutput.use_count() == 1)
{
// Last instance, deregister the handler
qInstallMessageHandler(0);
m_thisMessageOutput.reset();
}
}
void
MonitorQtOutput::qtMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
void MonitorQtOutput::qtMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
std::unique_lock<std::mutex> localLock(m_mutex);
auto globalMsgOut = m_qtMessageOutputForSharing.lock();
@ -50,23 +51,20 @@ MonitorQtOutput::qtMessageOutput(QtMsgType type, const QMessageLogContext &conte
m_previousHandler(type, context, msg);
}
QString
MonitorQtOutput::qtOuput()
QString MonitorQtOutput::qtOuput()
{
std::unique_lock<std::mutex> localLock(m_mutex);
assert(m_thisMessageOutput->length() >= m_start);
return m_thisMessageOutput->right(m_thisMessageOutput->length() - m_start);
}
void
MonitorQtOutput::restartCapturing()
void MonitorQtOutput::restartCapturing()
{
std::unique_lock<std::mutex> localLock(m_mutex);
// Ensure the messageHandler is installed. Foun to be reset at test initializaiton
auto prev = qInstallMessageHandler(qtMessageOutput);
if(prev != qtMessageOutput)
m_previousHandler = prev;
if(prev != qtMessageOutput) m_previousHandler = prev;
m_start = m_thisMessageOutput->length();
}
}
} // namespace Status::Testing

View File

@ -6,7 +6,8 @@
#include <memory>
#include <mutex>
namespace Status::Testing {
namespace Status::Testing
{
///
/// \brief Monitor output for tests and declaratively control message handler availability
@ -36,7 +37,7 @@ public:
signals:
private:
static void qtMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
static void qtMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg);
static QtMessageHandler m_previousHandler;
// Use it to keep track of qInstallMessageHandler call
@ -46,4 +47,4 @@ private:
int m_start = 0;
};
}
} // namespace Status::Testing

View File

@ -7,7 +7,8 @@
#include <QtQmlIntegration>
namespace Status::Wallet {
namespace Status::Wallet
{
/// Controlls asset for an account using hardcoded network and token lists
///
@ -15,7 +16,7 @@ namespace Status::Wallet {
/// \todo impliement \c AccountsBalanceService to fetch and cache realtime balance (or better implement this in status-go)
/// \todo implement native token fetching
/// \todo double responsibility, split functionality in asset management and balance
class AccountAssetsController: public QObject
class AccountAssetsController : public QObject
{
Q_OBJECT
QML_ELEMENT

View File

@ -6,7 +6,8 @@
namespace WalletGo = Status::StatusGo::Wallet;
namespace Status::Wallet {
namespace Status::Wallet
{
class DerivedWalletAddress : public QObject
{
@ -18,11 +19,14 @@ class DerivedWalletAddress : public QObject
Q_PROPERTY(bool alreadyCreated READ alreadyCreated CONSTANT)
public:
explicit DerivedWalletAddress(WalletGo::DerivedAddress address, QObject *parent = nullptr);
explicit DerivedWalletAddress(WalletGo::DerivedAddress address, QObject* parent = nullptr);
QString address() const;
const WalletGo::DerivedAddress &data() const { return m_derivedAddress; };
const WalletGo::DerivedAddress& data() const
{
return m_derivedAddress;
};
bool alreadyCreated() const;
@ -32,4 +36,4 @@ private:
using DerivedWalletAddressPtr = std::shared_ptr<DerivedWalletAddress>;
}
} // namespace Status::Wallet

View File

@ -1,17 +1,18 @@
#pragma once
#include "Status/Wallet/WalletAccount.h"
#include "Status/Wallet/DerivedWalletAddress.h"
#include "Status/Wallet/WalletAccount.h"
#include <Helpers/QObjectVectorModel.h>
#include <QtQmlIntegration>
namespace Status::Wallet {
namespace Status::Wallet
{
/// \note the following values are kept in sync \c selectedDerivedAddress, \c derivedAddressIndex and \c derivationPath
/// and \c customDerivationPath; \see connascence.io/value
class NewWalletAccountController: public QObject
class NewWalletAccountController : public QObject
{
Q_OBJECT
QML_ELEMENT
@ -20,7 +21,8 @@ class NewWalletAccountController: public QObject
Q_PROPERTY(QAbstractListModel* mainAccountsModel READ mainAccountsModel CONSTANT)
Q_PROPERTY(QAbstractItemModel* currentDerivedAddressModel READ currentDerivedAddressModel CONSTANT)
Q_PROPERTY(DerivedWalletAddress* selectedDerivedAddress READ selectedDerivedAddress WRITE setSelectedDerivedAddress NOTIFY selectedDerivedAddressChanged)
Q_PROPERTY(DerivedWalletAddress* selectedDerivedAddress READ selectedDerivedAddress WRITE setSelectedDerivedAddress
NOTIFY selectedDerivedAddressChanged)
Q_PROPERTY(int derivedAddressIndex MEMBER m_derivedAddressIndex NOTIFY selectedDerivedAddressChanged)
Q_PROPERTY(QString derivationPath READ derivationPath WRITE setDerivationPath NOTIFY derivationPathChanged)
@ -32,29 +34,29 @@ public:
/// \note On account creation \c accounts are updated with the newly created wallet account
NewWalletAccountController(std::shared_ptr<AccountsModel> accounts);
QAbstractListModel *mainAccountsModel();
QAbstractItemModel *currentDerivedAddressModel();
QAbstractListModel* mainAccountsModel();
QAbstractItemModel* currentDerivedAddressModel();
QString derivationPath() const;
void setDerivationPath(const QString &newDerivationPath);
void setDerivationPath(const QString& newDerivationPath);
/// \see \c accountCreatedStatus for async result
Q_INVOKABLE void createAccountAsync(const QString &password, const QString &name,
const QColor &color, const QString &path,
const Status::Wallet::WalletAccount *derivedFrom);
Q_INVOKABLE void createAccountAsync(const QString& password,
const QString& name,
const QColor& color,
const QString& path,
const Status::Wallet::WalletAccount* derivedFrom);
/// \see \c accountCreatedStatus for async result
Q_INVOKABLE void addWatchOnlyAccountAsync(const QString &address, const QString &name,
const QColor &color);
Q_INVOKABLE void addWatchOnlyAccountAsync(const QString& address, const QString& name, const QColor& color);
/// \returns \c false if fails (due to incomplete user input)
Q_INVOKABLE bool retrieveAndUpdateDerivedAddresses(const QString &password,
const Status::Wallet::WalletAccount *derivedFrom);
Q_INVOKABLE bool retrieveAndUpdateDerivedAddresses(const QString& password,
const Status::Wallet::WalletAccount* derivedFrom);
Q_INVOKABLE void clearDerivedAddresses();
DerivedWalletAddress *selectedDerivedAddress() const;
void setSelectedDerivedAddress(DerivedWalletAddress *newSelectedDerivedAddress);
DerivedWalletAddress* selectedDerivedAddress() const;
void setSelectedDerivedAddress(DerivedWalletAddress* newSelectedDerivedAddress);
signals:
void accountCreatedStatus(bool createdSuccessfully);
@ -68,11 +70,11 @@ signals:
private:
void updateSelectedDerivedAddress(int index, std::shared_ptr<DerivedWalletAddress> newEntry);
std::tuple<DerivedWalletAddressPtr, int> searchDerivationPath(const GoAccounts::DerivationPath &derivationPath);
std::tuple<DerivedWalletAddressPtr, int> searchDerivationPath(const GoAccounts::DerivationPath& derivationPath);
WalletAccountPtr findMissingAccount();
AccountsModel::ObjectContainer filterMainAccounts(const AccountsModel &accounts);
AccountsModel::ObjectContainer filterMainAccounts(const AccountsModel& accounts);
/// Logs a debug message if it fails
void addNewlyCreatedAccount(WalletAccountPtr newAccount);

View File

@ -4,7 +4,8 @@
#include <QtQmlIntegration>
namespace Status::Wallet {
namespace Status::Wallet
{
class SavedAddress : public QObject
{
@ -16,8 +17,7 @@ class SavedAddress : public QObject
Q_PROPERTY(QString name READ name CONSTANT)
public:
SavedAddress(const QString &address = QString(), const QString &name = QString(),
QObject *parent = nullptr);
SavedAddress(const QString& address = QString(), const QString& name = QString(), QObject* parent = nullptr);
const QString& address() const;
const QString& name() const;
@ -28,4 +28,4 @@ private:
};
using SavedAddressPtr = std::shared_ptr<SavedAddress>;
}
} // namespace Status::Wallet

View File

@ -15,7 +15,7 @@ class SavedAddressesController : public QObject
QML_ELEMENT
QML_UNCREATABLE("C++ only")
Q_PROPERTY(QAbstractListModel *savedAddresses READ savedAddresses CONSTANT)
Q_PROPERTY(QAbstractListModel* savedAddresses READ savedAddresses CONSTANT)
public:
enum Error
@ -26,11 +26,11 @@ public:
};
Q_ENUM(Error)
explicit SavedAddressesController(QObject *parent = nullptr);
explicit SavedAddressesController(QObject* parent = nullptr);
QAbstractListModel *savedAddresses() const;
QAbstractListModel* savedAddresses() const;
Q_INVOKABLE void saveAddress(const QString &address, const QString &name);
Q_INVOKABLE void saveAddress(const QString& address, const QString& name);
Q_INVOKABLE void refresh();
signals:

View File

@ -6,9 +6,10 @@
namespace GoAccounts = Status::StatusGo::Accounts;
namespace Status::Wallet {
namespace Status::Wallet
{
class WalletAccount: public QObject
class WalletAccount : public QObject
{
Q_OBJECT
QML_ELEMENT
@ -19,15 +20,18 @@ class WalletAccount: public QObject
Q_PROPERTY(QColor color READ color CONSTANT)
public:
explicit WalletAccount(const GoAccounts::ChatOrWalletAccount rawAccount, QObject *parent = nullptr);
explicit WalletAccount(const GoAccounts::ChatOrWalletAccount rawAccount, QObject* parent = nullptr);
const QString &name() const;
const QString& name() const;
const QString &strAddress() const;
const QString& strAddress() const;
QColor color() const;
const GoAccounts::ChatOrWalletAccount &data() const { return m_data; };
const GoAccounts::ChatOrWalletAccount& data() const
{
return m_data;
};
private:
const GoAccounts::ChatOrWalletAccount m_data;
@ -36,4 +40,4 @@ private:
using WalletAccountPtr = std::shared_ptr<WalletAccount>;
using WalletAccounts = std::vector<WalletAccount>;
}
} // namespace Status::Wallet

View File

@ -1,13 +1,14 @@
#pragma once
#include <StatusGo/Wallet/Token.h>
#include <StatusGo/Wallet/BigInt.h>
#include <StatusGo/Wallet/Token.h>
#include <QtQmlIntegration>
namespace WalletGo = Status::StatusGo::Wallet;
namespace Status::Wallet {
namespace Status::Wallet
{
class WalletAsset : public QObject
{
@ -22,7 +23,7 @@ class WalletAsset : public QObject
Q_PROPERTY(float value READ value CONSTANT)
public:
explicit WalletAsset(const WalletGo::TokenPtr token, StatusGo::Wallet::BigInt balance, QObject *parent = nullptr);
explicit WalletAsset(const WalletGo::TokenPtr token, StatusGo::Wallet::BigInt balance, QObject* parent = nullptr);
const QString name() const;
@ -42,4 +43,4 @@ private:
int m_count;
};
}
} // namespace Status::Wallet

View File

@ -11,14 +11,15 @@
class QQmlEngine;
class QJSEngine;
namespace Status::Wallet {
namespace Status::Wallet
{
class NewWalletAccountController;
class AccountAssetsController;
class SavedAddressesController;
/// \todo move account creation to its own controller
class WalletController: public QObject
class WalletController : public QObject
{
Q_OBJECT
QML_ELEMENT
@ -31,26 +32,27 @@ public:
WalletController();
/// Called by QML engine to register the instance. QML takes ownership of the instance
[[nodiscard]] static WalletController *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine);
[[nodiscard]] static WalletController* create(QQmlEngine* qmlEngine, QJSEngine* jsEngine);
/// To be used in the new wallet account workflow
/// \note caller (QML) takes ownership of the returned object
/// \todo consider if complex approach of keeping ownership here and enforcing a unique instance
/// or not reusing the account list and make it singleton are better options
Q_INVOKABLE [[nodiscard]] Status::Wallet::NewWalletAccountController *createNewWalletAccountController() const;
Q_INVOKABLE [[nodiscard]] Status::Wallet::NewWalletAccountController* createNewWalletAccountController() const;
/// Separated controler for working with wallet saved addresses
/// \note caller (QML) takes ownership of the returned object
/// \todo consider if complex approach of keeping ownership here and enforcing a unique instance
/// or not reusing the saved addresses list and make it singleton are better options
Q_INVOKABLE [[nodiscard]] Status::Wallet::SavedAddressesController *createSavedAddressesController() const;
Q_INVOKABLE [[nodiscard]] Status::Wallet::SavedAddressesController* createSavedAddressesController() const;
QAbstractListModel *accountsModel() const;
QAbstractListModel* accountsModel() const;
WalletAccount *currentAccount() const;
WalletAccount* currentAccount() const;
Q_INVOKABLE void setCurrentAccountIndex(int index);
/// \note caller (QML) takes ownership of the returned instance
Q_INVOKABLE Status::Wallet::AccountAssetsController* createAccountAssetsController(Status::Wallet::WalletAccount* account);
Q_INVOKABLE Status::Wallet::AccountAssetsController*
createAccountAssetsController(Status::Wallet::WalletAccount* account);
signals:
void currentAccountChanged();

View File

@ -1,8 +1,8 @@
#include "Status/Wallet/AccountAssetsController.h"
#include <StatusGo/Wallet/WalletApi.h>
#include <StatusGo/Wallet/BigInt.h>
#include <StatusGo/Metadata/api_response.h>
#include <StatusGo/Wallet/BigInt.h>
#include <StatusGo/Wallet/WalletApi.h>
#include <Helpers/helpers.h>
@ -10,7 +10,8 @@
namespace WalletGo = Status::StatusGo::Wallet;
namespace Status::Wallet {
namespace Status::Wallet
{
AccountAssetsController::AccountAssetsController(WalletAccount* address, QObject* parent)
: QObject(parent)
@ -18,7 +19,7 @@ AccountAssetsController::AccountAssetsController(WalletAccount* address, QObject
, m_enabledTokens({u"SNT"_qs, u"ETH"_qs, u"STT"_qs, u"DAI"_qs})
, m_assets(Helpers::makeSharedQObject<AssetModel>("asset"))
{
QtConcurrent::run([this, address]{ updateBalances(); })
QtConcurrent::run([this, address] { updateBalances(); })
.then([this] {
m_assetsReady = true;
emit assetsReadyChanged();
@ -30,56 +31,68 @@ AccountAssetsController::AccountAssetsController(WalletAccount* address, QObject
})
.onFailed([this] {
emit assetsReadyChanged();
qWarning() << "Unexpected failure while executing updateBalances for account" << m_address->data().address.get();
qWarning() << "Unexpected failure while executing updateBalances for account"
<< m_address->data().address.get();
});
}
void AccountAssetsController::updateBalances()
{
const StatusGo::Accounts::EOAddress& address = m_address->data().address;
if(m_assets->size() > 0)
m_assets->clear();
if(m_assets->size() > 0) m_assets->clear();
// TODO: this should be moved to status-go and exposed as "get balances for account and tokens with currency"
std::map<Accounts::EOAddress, StatusGo::Wallet::Token> tokens;
std::vector<WalletGo::ChainID> chainIds;
auto allNets = WalletGo::getEthereumChains(false);
for(const auto &net : allNets) {
if(net.enabled && !net.isTest) {
try {
for(const auto& net : allNets)
{
if(net.enabled && !net.isTest)
{
try
{
const auto allTokens = WalletGo::getTokens(net.chainId);
for(const auto& tokenToMove : allTokens) {
if(isTokenEnabled(tokenToMove)) {
for(const auto& tokenToMove : allTokens)
{
if(isTokenEnabled(tokenToMove))
{
auto address = tokenToMove.address;
tokens.emplace(std::move(address), std::move(tokenToMove));
}
}
chainIds.push_back(net.chainId);
}
catch (const StatusGo::CallPrivateRpcError& e) {
catch(const StatusGo::CallPrivateRpcError& e)
{
// Most probably "no tokens for this network"
if(e.errorResponse().error.message.compare("no tokens for this network") != 0)
qWarning() << "Failed retrieving tokens for network" << net.chainId.get() << "; error" << e.errorResponse().error.message.c_str();
qWarning() << "Failed retrieving tokens for network" << net.chainId.get() << "; error"
<< e.errorResponse().error.message.c_str();
continue;
}
}
}
auto accountBalances = WalletGo::getTokensBalancesForChainIDs(chainIds, { address }, std::move(Helpers::getKeys(tokens)));
if(accountBalances.size() == 1) {
for(const auto& accountAndBalance : accountBalances.begin()->second) {
auto asset = Helpers::makeSharedQObject<WalletAsset>(std::make_shared<WalletGo::Token>(tokens.at(accountAndBalance.first)), accountAndBalance.second);
auto accountBalances =
WalletGo::getTokensBalancesForChainIDs(chainIds, {address}, std::move(Helpers::getKeys(tokens)));
if(accountBalances.size() == 1)
{
for(const auto& accountAndBalance : accountBalances.begin()->second)
{
auto asset = Helpers::makeSharedQObject<WalletAsset>(
std::make_shared<WalletGo::Token>(tokens.at(accountAndBalance.first)), accountAndBalance.second);
m_assets->push_back(asset);
}
}
else
qWarning() << "Failed fetching balances for account" << address.get() << "; balances count" << accountBalances.size();
qWarning() << "Failed fetching balances for account" << address.get() << "; balances count"
<< accountBalances.size();
}
bool AccountAssetsController::isTokenEnabled(const StatusGo::Wallet::Token& token) const
{
return find_if(m_enabledTokens.begin(), m_enabledTokens.end(), [&token](const auto& symbol) {
return token.symbol == symbol;
}) != m_enabledTokens.end();
return token.symbol == symbol;
}) != m_enabledTokens.end();
}
QAbstractItemModel* AccountAssetsController::assetsModel() const
@ -97,4 +110,4 @@ bool AccountAssetsController::assetsReady() const
return m_assetsReady;
}
}
} // namespace Status::Wallet

View File

@ -1,12 +1,12 @@
#include "DerivedWalletAddress.h"
namespace Status::Wallet {
namespace Status::Wallet
{
DerivedWalletAddress::DerivedWalletAddress(WalletGo::DerivedAddress address, QObject *parent)
DerivedWalletAddress::DerivedWalletAddress(WalletGo::DerivedAddress address, QObject* parent)
: QObject{parent}
, m_derivedAddress{std::move(address)}
{
}
{ }
QString DerivedWalletAddress::address() const
{
@ -18,4 +18,4 @@ bool DerivedWalletAddress::alreadyCreated() const
return m_derivedAddress.alreadyCreated;
}
}
} // namespace Status::Wallet

View File

@ -2,17 +2,17 @@
#include <StatusGo/Wallet/WalletApi.h>
#include <StatusGo/Accounts/AccountsAPI.h>
#include <StatusGo/Accounts/Accounts.h>
#include <StatusGo/Accounts/AccountsAPI.h>
#include <StatusGo/Accounts/accounts_types.h>
#include <StatusGo/Metadata/api_response.h>
#include <StatusGo/Utils.h>
#include <StatusGo/Types.h>
#include <StatusGo/Utils.h>
#include <Onboarding/Common/Constants.h>
#include <QQmlEngine>
#include <QJSEngine>
#include <QQmlEngine>
#include <ranges>
@ -21,22 +21,23 @@ namespace WalletGo = Status::StatusGo::Wallet;
namespace UtilsSG = Status::StatusGo::Utils;
namespace StatusGo = Status::StatusGo;
namespace Status::Wallet {
namespace Status::Wallet
{
NewWalletAccountController::NewWalletAccountController(std::shared_ptr<Helpers::QObjectVectorModel<WalletAccount>> accounts)
NewWalletAccountController::NewWalletAccountController(
std::shared_ptr<Helpers::QObjectVectorModel<WalletAccount>> accounts)
: m_accounts(accounts)
, m_mainAccounts(std::move(filterMainAccounts(*accounts)), "account")
, m_derivedAddress("derivedAddress")
, m_derivationPath(Status::Constants::General::PathWalletRoot)
{
}
{ }
QAbstractListModel* NewWalletAccountController::mainAccountsModel()
{
return &m_mainAccounts;
}
QAbstractItemModel *NewWalletAccountController::currentDerivedAddressModel()
QAbstractItemModel* NewWalletAccountController::currentDerivedAddressModel()
{
return &m_derivedAddress;
}
@ -46,77 +47,92 @@ QString NewWalletAccountController::derivationPath() const
return m_derivationPath.get();
}
void NewWalletAccountController::setDerivationPath(const QString &newDerivationPath)
void NewWalletAccountController::setDerivationPath(const QString& newDerivationPath)
{
if (m_derivationPath.get() == newDerivationPath)
return;
if(m_derivationPath.get() == newDerivationPath) return;
m_derivationPath = GoAccounts::DerivationPath(newDerivationPath);
emit derivationPathChanged();
auto oldCustom = m_customDerivationPath;
const auto &[derivedPath, index]= searchDerivationPath(m_derivationPath);
const auto& [derivedPath, index] = searchDerivationPath(m_derivationPath);
m_customDerivationPath = derivedPath == nullptr;
if(!m_customDerivationPath && !derivedPath.get()->alreadyCreated())
updateSelectedDerivedAddress(index, derivedPath);
if(m_customDerivationPath != oldCustom)
emit customDerivationPathChanged();
if(m_customDerivationPath != oldCustom) emit customDerivationPathChanged();
}
void NewWalletAccountController::createAccountAsync(const QString &password, const QString &name,
const QColor &color, const QString &path,
const WalletAccount *derivedFrom)
void NewWalletAccountController::createAccountAsync(const QString& password,
const QString& name,
const QColor& color,
const QString& path,
const WalletAccount* derivedFrom)
{
try {
try
{
GoAccounts::generateAccountWithDerivedPath(StatusGo::HashedPassword(UtilsSG::hashPassword(password)),
name, color, "", GoAccounts::DerivationPath(path),
name,
color,
"",
GoAccounts::DerivationPath(path),
derivedFrom->data().derivedFrom.value());
addNewlyCreatedAccount(findMissingAccount());
}
catch(const StatusGo::CallPrivateRpcError& e) {
catch(const StatusGo::CallPrivateRpcError& e)
{
qWarning() << "StatusGoQt.generateAccountWithDerivedPath error: " << e.errorResponse().error.message.c_str();
emit accountCreatedStatus(false);
}
}
void NewWalletAccountController::addWatchOnlyAccountAsync(const QString &address, const QString &name, const QColor &color)
void NewWalletAccountController::addWatchOnlyAccountAsync(const QString& address,
const QString& name,
const QColor& color)
{
try {
try
{
GoAccounts::addAccountWatch(Accounts::EOAddress(address), name, color, u""_qs);
addNewlyCreatedAccount(findMissingAccount());
}
catch(const StatusGo::CallPrivateRpcError& e) {
catch(const StatusGo::CallPrivateRpcError& e)
{
qWarning() << "StatusGoQt.generateAccountWithDerivedPath error: " << e.errorResponse().error.message.c_str();
emit accountCreatedStatus(false);
}
}
bool NewWalletAccountController::retrieveAndUpdateDerivedAddresses(const QString &password,
const WalletAccount *derivedFrom)
bool NewWalletAccountController::retrieveAndUpdateDerivedAddresses(const QString& password,
const WalletAccount* derivedFrom)
{
assert(derivedFrom->data().derivedFrom.has_value());
try {
try
{
int currentPage = 1;
int foundIndex = -1;
int currentIndex = 0;
auto maxPageCount = static_cast<int>(std::ceil(static_cast<double>(m_maxDerivedAddresses)/static_cast<double>(m_derivedAddressesPageSize)));
auto maxPageCount = static_cast<int>(
std::ceil(static_cast<double>(m_maxDerivedAddresses) / static_cast<double>(m_derivedAddressesPageSize)));
std::shared_ptr<DerivedWalletAddress> foundEntry;
while(currentPage <= maxPageCount && foundIndex < 0) {
while(currentPage <= maxPageCount && foundIndex < 0)
{
auto all = WalletGo::getDerivedAddressesForPath(StatusGo::HashedPassword(UtilsSG::hashPassword(password)),
derivedFrom->data().derivedFrom.value(),
Status::Constants::General::PathWalletRoot,
m_derivedAddressesPageSize, currentPage);
m_derivedAddressesPageSize,
currentPage);
if((currentIndex + all.size()) > m_derivedAddress.size())
m_derivedAddress.resize(currentIndex + all.size());
for(auto newDerived : all) {
for(auto newDerived : all)
{
auto newEntry = Helpers::makeSharedQObject<DerivedWalletAddress>(std::move(newDerived));
m_derivedAddress.set(currentIndex, newEntry);
if(foundIndex < 0 && !newEntry->data().alreadyCreated) {
if(foundIndex < 0 && !newEntry->data().alreadyCreated)
{
foundIndex = currentIndex;
foundEntry = newEntry;
}
@ -124,11 +140,12 @@ bool NewWalletAccountController::retrieveAndUpdateDerivedAddresses(const QString
}
currentPage++;
}
if(foundIndex > 0)
updateSelectedDerivedAddress(foundIndex, foundEntry);
if(foundIndex > 0) updateSelectedDerivedAddress(foundIndex, foundEntry);
return true;
} catch(const StatusGo::CallPrivateRpcError &e) {
}
catch(const StatusGo::CallPrivateRpcError& e)
{
return false;
}
}
@ -142,19 +159,20 @@ WalletAccountPtr NewWalletAccountController::findMissingAccount()
{
auto accounts = GoAccounts::getAccounts();
// TODO: consider using a QObjectSetModel and a proxy sort model on top instead
auto it = std::find_if(accounts.begin(), accounts.end(), [this](const auto &a) {
return std::none_of(m_accounts->objects().begin(), m_accounts->objects().end(),
[&a](const auto &eA) { return a.address == eA->data().address; });
auto it = std::find_if(accounts.begin(), accounts.end(), [this](const auto& a) {
return std::none_of(m_accounts->objects().begin(), m_accounts->objects().end(), [&a](const auto& eA) {
return a.address == eA->data().address;
});
});
return it != accounts.end() ? Helpers::makeSharedQObject<WalletAccount>(*it) : nullptr;
}
NewWalletAccountController::AccountsModel::ObjectContainer
NewWalletAccountController::filterMainAccounts(const AccountsModel &accounts)
NewWalletAccountController::filterMainAccounts(const AccountsModel& accounts)
{
AccountsModel::ObjectContainer out;
const auto &c = accounts.objects();
std::copy_if(c.begin(), c.end(), std::back_inserter(out), [](const auto &a){ return a->data().isWallet; });
const auto& c = accounts.objects();
std::copy_if(c.begin(), c.end(), std::back_inserter(out), [](const auto& a) { return a->data().isWallet; });
return out;
}
@ -168,36 +186,41 @@ void NewWalletAccountController::addNewlyCreatedAccount(WalletAccountPtr newAcco
emit accountCreatedStatus(newAccount != nullptr);
}
DerivedWalletAddress *NewWalletAccountController::selectedDerivedAddress() const
DerivedWalletAddress* NewWalletAccountController::selectedDerivedAddress() const
{
return m_selectedDerivedAddress.get();
}
void NewWalletAccountController::setSelectedDerivedAddress(DerivedWalletAddress *newSelectedDerivedAddress)
void NewWalletAccountController::setSelectedDerivedAddress(DerivedWalletAddress* newSelectedDerivedAddress)
{
if (m_selectedDerivedAddress.get() == newSelectedDerivedAddress)
return;
auto &objs = m_derivedAddress.objects();
auto foundIt = std::find_if(objs.begin(), objs.end(), [newSelectedDerivedAddress](const auto &a) { return a.get() == newSelectedDerivedAddress; });
if(m_selectedDerivedAddress.get() == newSelectedDerivedAddress) return;
auto& objs = m_derivedAddress.objects();
auto foundIt = std::find_if(objs.begin(), objs.end(), [newSelectedDerivedAddress](const auto& a) {
return a.get() == newSelectedDerivedAddress;
});
updateSelectedDerivedAddress(std::distance(objs.begin(), foundIt), *foundIt);
}
void NewWalletAccountController::updateSelectedDerivedAddress(int index, std::shared_ptr<DerivedWalletAddress> newEntry) {
void NewWalletAccountController::updateSelectedDerivedAddress(int index, std::shared_ptr<DerivedWalletAddress> newEntry)
{
m_derivedAddressIndex = index;
m_selectedDerivedAddress = newEntry;
emit selectedDerivedAddressChanged();
if(m_derivationPath != newEntry->data().path) {
if(m_derivationPath != newEntry->data().path)
{
m_derivationPath = newEntry->data().path;
emit derivationPathChanged();
}
}
std::tuple<DerivedWalletAddressPtr, int> NewWalletAccountController::searchDerivationPath(const GoAccounts::DerivationPath &derivationPath) {
const auto &c = m_derivedAddress.objects();
auto foundIt = find_if(c.begin(), c.end(), [&derivationPath](const auto &a) { return a->data().path == derivationPath; });
if(foundIt != c.end())
return {*foundIt, std::distance(c.begin(), foundIt)};
std::tuple<DerivedWalletAddressPtr, int>
NewWalletAccountController::searchDerivationPath(const GoAccounts::DerivationPath& derivationPath)
{
const auto& c = m_derivedAddress.objects();
auto foundIt =
find_if(c.begin(), c.end(), [&derivationPath](const auto& a) { return a->data().path == derivationPath; });
if(foundIt != c.end()) return {*foundIt, std::distance(c.begin(), foundIt)};
return {nullptr, -1};
}
}
} // namespace Status::Wallet

View File

@ -3,12 +3,11 @@
namespace Status::Wallet
{
SavedAddress::SavedAddress(const QString &address, const QString &name, QObject *parent)
SavedAddress::SavedAddress(const QString& address, const QString& name, QObject* parent)
: QObject(parent)
, m_address(address)
, m_name(name)
{
}
{ }
const QString& SavedAddress::address() const
{
@ -20,4 +19,4 @@ const QString& SavedAddress::name() const
return m_name;
}
}
} // namespace Status::Wallet

Some files were not shown because too many files have changed in this diff Show More