chore(CPP): Enhance type safety using phantom types

Important changes:

- Converted the easy to mix strings to named types as phantom types
- Renamed `AccountDto`s to `MultiAccount` to better match the status-go
domain knowledge.
- Renamed MultiAccount to ChatOrWalletAccount to better match its multi
purpose and don't confuse with the MultiAccount domain knowledge.
- Remove libs/CMakeLists.txt

Note: Tried to use the fluent::NamedType but it doesn't work with
nlohmann_json, gave up finding why. Therefore I extracted only
the needed functionality for the simple types we use.

Updates: #6321
This commit is contained in:
Stefan 2022-07-13 18:45:18 +02:00 committed by Stefan Dunca
parent a130681dd5
commit 16b866ccbd
53 changed files with 383 additions and 289 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ noBackup/
*.log *.log
.update.timestamp .update.timestamp
.vscode .vscode
*.code-workspace
.tours .tours
bin/ bin/
/bottles/ /bottles/

View File

@ -27,9 +27,18 @@ endif()
# status-desktop application # status-desktop application
add_subdirectory(vendor) add_subdirectory(vendor)
add_subdirectory(libs)
add_subdirectory(libs/ApplicationCore)
add_subdirectory(libs/Assets)
add_subdirectory(libs/Helpers)
add_subdirectory(libs/Onboarding)
add_subdirectory(libs/StatusGoQt)
add_subdirectory(libs/StatusQ)
add_subdirectory(app) add_subdirectory(app)
add_subdirectory(test/libs/StatusGoQt) add_subdirectory(test/libs/StatusGoQt)
# TODO: temporary not to duplicate resources until we switch to c++ app then it can be refactored # TODO: temporary not to duplicate resources until we switch to c++ app then it can be refactored
add_subdirectory(resources) add_subdirectory(resources)
add_subdirectory(ui/imports/assets) add_subdirectory(ui/imports/assets)

View File

@ -32,6 +32,9 @@ qt6_add_qml_module(${PROJECT_NAME}
qml/Status/Application/MainShortcuts.qml qml/Status/Application/MainShortcuts.qml
qml/Status/Application/StatusWindow.qml qml/Status/Application/StatusWindow.qml
SOURCES
res/app.qrc
OUTPUT_DIRECTORY OUTPUT_DIRECTORY
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Status/Application ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Status/Application
) )
@ -47,7 +50,6 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE BUILD_SOURCE_DIR=${CMAKE_SOUR
add_subdirectory(qml/Status/Application/Navigation) add_subdirectory(qml/Status/Application/Navigation)
add_subdirectory(src) add_subdirectory(src)
add_subdirectory(res)
include(${CMAKE_SOURCE_DIR}/cmake/platform_specific.cmake) include(${CMAKE_SOURCE_DIR}/cmake/platform_specific.cmake)
string(TOLOWER ${PROJECT_ORGANIZATION_NAME} URL_ORGANIZATION_NAME) string(TOLOWER ${PROJECT_ORGANIZATION_NAME} URL_ORGANIZATION_NAME)

View File

@ -14,7 +14,6 @@ qt6_add_qml_module(${PROJECT_NAME}
URI Status.Application.Navigation URI Status.Application.Navigation
VERSION 1.0 VERSION 1.0
# TODO: temporary until we make qt_target_qml_sources work
QML_FILES QML_FILES
StatusNavigationBar.qml StatusNavigationBar.qml
StatusNavigationButton.qml StatusNavigationButton.qml

View File

@ -1,15 +0,0 @@
# TODO workaround until Qt6 API is clarified
target_sources(${PROJECT_NAME}
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/app.qrc
${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
)
# TODO find out why it isn't working
#
#qt6_target_qml_sources(${PROJECT_NAME}
# RESOURCES
# qtquickcontrols2.conf
# PREFIX ""
#)

View File

@ -20,7 +20,6 @@ qt6_add_qml_module(Assets
URI Status.Assets URI Status.Assets
VERSION 1.0 VERSION 1.0
# TODO: temporary until we make qt_target_qml_sources work
QML_FILES QML_FILES
qml/Status/Assets/Resources.qml qml/Status/Assets/Resources.qml

View File

@ -1,6 +0,0 @@
add_subdirectory(ApplicationCore)
add_subdirectory(Assets)
add_subdirectory(Helpers)
add_subdirectory(Onboarding)
add_subdirectory(StatusGoQt)
add_subdirectory(StatusQ)

View File

@ -71,5 +71,6 @@ target_sources(Helpers
${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/helpers.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/helpers.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/logs.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/logs.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/logs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/logs.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/NamedType.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/Singleton.h ${CMAKE_CURRENT_SOURCE_DIR}/src/Helpers/Singleton.h
) )

View File

@ -0,0 +1,70 @@
#pragma once
#include <nlohmann/json.hpp>
#include <type_traits>
#include <utility>
using json = nlohmann::json;
namespace Status::Helpers {
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
template <typename T, typename Parameter>
class NamedType
{
public:
using UnderlyingType = T;
// constructor
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;
// get
constexpr T& get() { return m_value; }
constexpr std::remove_reference_t<T> const& get() const {return m_value; }
bool operator<(const NamedType<T, Parameter> &) const = default;
bool operator>(const NamedType<T, Parameter> &) const = default;
bool operator<=(const NamedType<T, Parameter> &) const = default;
bool operator>=(const NamedType<T, Parameter> &) const = default;
bool operator==(const NamedType<T, Parameter> &) const = default;
bool operator!=(const NamedType<T, Parameter> &) const = default;
private:
T m_value;
};
template <typename T, typename 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) {
p = NamedType<T, P>{j.get<T>()};
}
}
namespace std
{
template <typename T, typename Parameter>
struct hash<Status::Helpers::NamedType<T, Parameter>>
{
using NamedType = Status::Helpers::NamedType<T, Parameter>;
using checkIfHashable = typename std::enable_if<NamedType::is_hashable, void>::type;
size_t operator()(NamedType const& x) const
{
return std::hash<T>()(x.get());
}
};
}

View File

@ -4,13 +4,8 @@ namespace fs = std::filesystem;
namespace Status { namespace Status {
QString toQString(const std::string &str)
{
return QString::fromStdString(str);
}
QString toQString(const fs::path &path) { QString toQString(const fs::path &path) {
return toQString(path.string()); return QString::fromStdString(path.string());
} }
fs::path toPath(const QString &pathStr) { fs::path toPath(const QString &pathStr) {

View File

@ -12,8 +12,8 @@ using json = nlohmann::json;
namespace Status { namespace Status {
QString toQString(const std::string& str);
QString toQString(const std::filesystem::path& path); QString toQString(const std::filesystem::path& path);
std::filesystem::path toPath(const QString& pathStr); std::filesystem::path toPath(const QString& pathStr);
} // namespace Status } // namespace Status
@ -42,4 +42,15 @@ struct adl_serializer<QColor> {
} }
}; };
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) {
opt.emplace(j.get<T>());
}
};
} // namespace nlohmann } // namespace nlohmann

View File

@ -37,6 +37,9 @@ add_subdirectory(src)
add_subdirectory(tests) add_subdirectory(tests)
target_link_libraries(Onboarding target_link_libraries(Onboarding
PUBLIC
Status::StatusGoQt
PRIVATE PRIVATE
Qt6::Quick Qt6::Quick
Qt6::Qml Qt6::Qml
@ -45,7 +48,6 @@ target_link_libraries(Onboarding
Status::ApplicationCore Status::ApplicationCore
Status::Helpers Status::Helpers
Status::StatusGoQt
Status::StatusGoConfig Status::StatusGoConfig
) )

View File

@ -11,9 +11,6 @@ SetupNewProfilePageBase {
TempTextInput { TempTextInput {
id: confirmPasswordInput id: confirmPasswordInput
// TODO: remove this developer helper
text: qsTr("1234567890")
width: 416 width: 416
height: 44 height: 44

View File

@ -26,12 +26,12 @@ getDataFromFile(const fs::path &path)
return data; return data;
} }
namespace Status::Onboarding
{
namespace StatusGo = Status::StatusGo; namespace StatusGo = Status::StatusGo;
namespace Utils = Status::StatusGo::Utils; namespace Utils = Status::StatusGo::Utils;
namespace Status::Onboarding
{
AccountsService::AccountsService() AccountsService::AccountsService()
: m_isFirstTimeAccountLogin(false) : m_isFirstTimeAccountLogin(false)
{ {
@ -49,32 +49,32 @@ bool AccountsService::init(const fs::path& statusgoDataDir)
for(const auto &genAddressObj : response.result) for(const auto &genAddressObj : response.result)
{ {
auto gAcc = GeneratedAccountDto::toGeneratedAccountDto(genAddressObj.toObject()); auto gAcc = GeneratedMultiAccount::toGeneratedMultiAccount(genAddressObj.toObject());
gAcc.alias = generateAlias(gAcc.derivedAccounts.whisper.publicKey); gAcc.alias = generateAlias(gAcc.derivedAccounts.whisper.publicKey);
m_generatedAccounts.push_back(std::move(gAcc)); m_generatedAccounts.push_back(std::move(gAcc));
} }
return true; return true;
} }
std::vector<AccountDto> AccountsService::openAndListAccounts() std::vector<MultiAccount> AccountsService::openAndListAccounts()
{ {
auto response = StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()); auto response = StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str());
if(response.containsError()) if(response.containsError())
{ {
qWarning() << response.error.message; qWarning() << response.error.message;
return std::vector<AccountDto>(); return std::vector<MultiAccount>();
} }
const auto multiAccounts = response.result; const auto multiAccounts = response.result;
std::vector<AccountDto> result; std::vector<MultiAccount> result;
for(const auto &value : multiAccounts) for(const auto &value : multiAccounts)
{ {
result.push_back(AccountDto::toAccountDto(value.toObject())); result.push_back(MultiAccount::toMultiAccount(value.toObject()));
} }
return result; return result;
} }
const std::vector<GeneratedAccountDto>& AccountsService::generatedAccounts() const const std::vector<GeneratedMultiAccount>& AccountsService::generatedAccounts() const
{ {
return m_generatedAccounts; return m_generatedAccounts;
} }
@ -91,7 +91,7 @@ bool AccountsService::setupAccountAndLogin(const QString &accountId, const QStri
QJsonObject settings(getAccountSettings(accountId, installationId, displayName)); QJsonObject settings(getAccountSettings(accountId, installationId, displayName));
QJsonObject nodeConfig(getDefaultNodeConfig(installationId)); QJsonObject nodeConfig(getDefaultNodeConfig(installationId));
QString hashedPassword(Utils::hashString(password)); auto hashedPassword(Utils::hashPassword(password));
// This initialize the DB if first time running. Required for storing accounts // This initialize the DB if first time running. Required for storing accounts
if(StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()).containsError()) if(StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()).containsError())
@ -105,12 +105,12 @@ bool AccountsService::setupAccountAndLogin(const QString &accountId, const QStri
return getLoggedInAccount().isValid(); return getLoggedInAccount().isValid();
} }
const AccountDto& AccountsService::getLoggedInAccount() const const MultiAccount& AccountsService::getLoggedInAccount() const
{ {
return m_loggedInAccount; return m_loggedInAccount;
} }
const GeneratedAccountDto& AccountsService::getImportedAccount() const const GeneratedMultiAccount& AccountsService::getImportedAccount() const
{ {
return m_importedAccount; return m_importedAccount;
} }
@ -127,7 +127,7 @@ bool AccountsService::setKeyStoreDir(const QString &key)
return !response.containsError(); return !response.containsError();
} }
QString AccountsService::login(AccountDto account, const QString& password) QString AccountsService::login(MultiAccount account, const QString& password)
{ {
// This is a requirement. Make it more explicit into the status go module // This is a requirement. Make it more explicit into the status go module
if(!setKeyStoreDir(account.keyUid)) if(!setKeyStoreDir(account.keyUid))
@ -137,7 +137,7 @@ QString AccountsService::login(AccountDto account, const QString& password)
if(StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()).containsError()) if(StatusGo::Accounts::openAccounts(m_statusgoDataDir.c_str()).containsError())
return QString("Failed to open accounts before logging in"); return QString("Failed to open accounts before logging in");
QString hashedPassword(Utils::hashString(password)); auto hashedPassword(Utils::hashPassword(password));
QString thumbnailImage; QString thumbnailImage;
QString largeImage; QString largeImage;
@ -157,8 +157,8 @@ QString AccountsService::login(AccountDto account, const QString& password)
void AccountsService::clear() void AccountsService::clear()
{ {
m_generatedAccounts.clear(); m_generatedAccounts.clear();
m_loggedInAccount = AccountDto(); m_loggedInAccount = MultiAccount();
m_importedAccount = GeneratedAccountDto(); m_importedAccount = GeneratedMultiAccount();
m_isFirstTimeAccountLogin = false; m_isFirstTimeAccountLogin = false;
} }
@ -174,15 +174,15 @@ QString AccountsService::generateAlias(const QString& publicKey)
return response.result; return response.result;
} }
void AccountsService::deleteMultiAccount(const AccountDto &account) void AccountsService::deleteMultiAccount(const MultiAccount &account)
{ {
StatusGo::Accounts::deleteMultiaccount(account.keyUid, m_keyStoreDir); StatusGo::Accounts::deleteMultiaccount(account.keyUid, m_keyStoreDir);
} }
DerivedAccounts AccountsService::storeDerivedAccounts(const QString& accountId, const QString& hashedPassword, DerivedAccounts AccountsService::storeDerivedAccounts(const QString& accountId, const StatusGo::HashedPassword& password,
const QVector<QString>& paths) const std::vector<Accounts::DerivationPath> &paths)
{ {
auto response = StatusGo::Accounts::storeDerivedAccounts(accountId, hashedPassword, paths); auto response = StatusGo::Accounts::storeDerivedAccounts(accountId, password, paths);
if(response.containsError()) if(response.containsError())
{ {
qWarning() << response.error.message; qWarning() << response.error.message;
@ -191,31 +191,31 @@ DerivedAccounts AccountsService::storeDerivedAccounts(const QString& accountId,
return DerivedAccounts::toDerivedAccounts(response.result); return DerivedAccounts::toDerivedAccounts(response.result);
} }
StoredAccountDto AccountsService::storeAccount(const QString& accountId, const QString& hashedPassword) StoredMultiAccount AccountsService::storeAccount(const QString& accountId, const StatusGo::HashedPassword& password)
{ {
auto response = StatusGo::Accounts::storeAccount(accountId, hashedPassword); auto response = StatusGo::Accounts::storeAccount(accountId, password);
if(response.containsError()) if(response.containsError())
{ {
qWarning() << response.error.message; qWarning() << response.error.message;
return StoredAccountDto(); return StoredMultiAccount();
} }
return toStoredAccountDto(response.result); return toStoredMultiAccount(response.result);
} }
AccountDto AccountsService::saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, MultiAccount AccountsService::saveAccountAndLogin(const StatusGo::HashedPassword& password, const QJsonObject& account,
const QJsonArray& subaccounts, const QJsonObject& settings, const QJsonArray& subaccounts, const QJsonObject& settings,
const QJsonObject& config) const QJsonObject& config)
{ {
if(!StatusGo::Accounts::saveAccountAndLogin(hashedPassword, account, subaccounts, settings, config)) { if(!StatusGo::Accounts::saveAccountAndLogin(password, account, subaccounts, settings, config)) {
qWarning() << "Failed saving acccount" << account.value("name"); qWarning() << "Failed saving acccount" << account.value("name");
return AccountDto(); return MultiAccount();
} }
m_isFirstTimeAccountLogin = true; m_isFirstTimeAccountLogin = true;
return AccountDto::toAccountDto(account); return MultiAccount::toMultiAccount(account);
} }
QJsonObject AccountsService::prepareAccountJsonObject(const GeneratedAccountDto& account, const QString &displayName) const QJsonObject AccountsService::prepareAccountJsonObject(const GeneratedMultiAccount& account, const QString &displayName) const
{ {
return QJsonObject{{"name", displayName.isEmpty() ? account.alias : displayName}, return QJsonObject{{"name", displayName.isEmpty() ? account.alias : displayName},
{"address", account.address}, {"address", account.address},
@ -225,7 +225,7 @@ QJsonObject AccountsService::prepareAccountJsonObject(const GeneratedAccountDto&
QJsonObject AccountsService::getAccountDataForAccountId(const QString &accountId, const QString &displayName) const QJsonObject AccountsService::getAccountDataForAccountId(const QString &accountId, const QString &displayName) const
{ {
for(const GeneratedAccountDto &acc : m_generatedAccounts) for(const GeneratedMultiAccount &acc : m_generatedAccounts)
{ {
if(acc.id == accountId) if(acc.id == accountId)
{ {
@ -245,7 +245,7 @@ QJsonObject AccountsService::getAccountDataForAccountId(const QString &accountId
return QJsonObject(); return QJsonObject();
} }
QJsonArray AccountsService::prepareSubaccountJsonObject(const GeneratedAccountDto& account, const QString &displayName) const QJsonArray AccountsService::prepareSubaccountJsonObject(const GeneratedMultiAccount& account, const QString &displayName) const
{ {
return { return {
QJsonObject{ QJsonObject{
@ -253,7 +253,7 @@ QJsonArray AccountsService::prepareSubaccountJsonObject(const GeneratedAccountDt
{"address", account.derivedAccounts.defaultWallet.address}, {"address", account.derivedAccounts.defaultWallet.address},
{"color", "#4360df"}, {"color", "#4360df"},
{"wallet", true}, {"wallet", true},
{"path", Constants::General::PathDefaultWallet}, {"path", Constants::General::PathDefaultWallet.get()},
{"name", "Status account"}, {"name", "Status account"},
{"derived-from", account.address} {"derived-from", account.address}
}, },
@ -261,7 +261,7 @@ QJsonArray AccountsService::prepareSubaccountJsonObject(const GeneratedAccountDt
{"public-key", account.derivedAccounts.whisper.publicKey}, {"public-key", account.derivedAccounts.whisper.publicKey},
{"address", account.derivedAccounts.whisper.address}, {"address", account.derivedAccounts.whisper.address},
{"name", displayName.isEmpty() ? account.alias : displayName}, {"name", displayName.isEmpty() ? account.alias : displayName},
{"path", Constants::General::PathWhisper}, {"path", Constants::General::PathWhisper.get()},
{"chat", true}, {"chat", true},
{"derived-from", ""} {"derived-from", ""}
} }
@ -271,7 +271,7 @@ QJsonArray AccountsService::prepareSubaccountJsonObject(const GeneratedAccountDt
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 :)" // "All these for loops with a nested if cry for a std::find_if :)"
for(const GeneratedAccountDto &acc : m_generatedAccounts) for(const GeneratedMultiAccount &acc : m_generatedAccounts)
{ {
if(acc.id == accountId) if(acc.id == accountId)
{ {
@ -302,7 +302,7 @@ QString AccountsService::generateSigningPhrase(int count) const
return words.join(" "); return words.join(" ");
} }
QJsonObject AccountsService::prepareAccountSettingsJsonObject(const GeneratedAccountDto& account, QJsonObject AccountsService::prepareAccountSettingsJsonObject(const GeneratedMultiAccount& account,
const QString& installationId, const QString& installationId,
const QString& displayName) const const QString& displayName) const
{ {
@ -348,7 +348,7 @@ QJsonObject AccountsService::prepareAccountSettingsJsonObject(const GeneratedAcc
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 GeneratedAccountDto &acc : m_generatedAccounts) for(const GeneratedMultiAccount &acc : m_generatedAccounts)
if(acc.id == accountId) if(acc.id == accountId)
{ {

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <StatusGo/Types.h>
#include "AccountsServiceInterface.h" #include "AccountsServiceInterface.h"
namespace Status::Onboarding namespace Status::Onboarding
@ -25,18 +27,18 @@ public:
bool init(const fs::path& statusgoDataDir) override; bool init(const fs::path& statusgoDataDir) override;
/// \see ServiceInterface /// \see ServiceInterface
[[nodiscard]] std::vector<AccountDto> openAndListAccounts() override; [[nodiscard]] std::vector<MultiAccount> openAndListAccounts() override;
/// \see ServiceInterface /// \see ServiceInterface
[[nodiscard]] const std::vector<GeneratedAccountDto>& generatedAccounts() const override; [[nodiscard]] const std::vector<GeneratedMultiAccount>& generatedAccounts() const override;
/// \see ServiceInterface /// \see ServiceInterface
bool setupAccountAndLogin(const QString& accountId, const QString& password, const QString& displayName) override; bool setupAccountAndLogin(const QString& accountId, const QString& password, const QString& displayName) override;
/// \see ServiceInterface /// \see ServiceInterface
[[nodiscard]] const AccountDto& getLoggedInAccount() const override; [[nodiscard]] const MultiAccount& getLoggedInAccount() const override;
[[nodiscard]] const GeneratedAccountDto& getImportedAccount() const override; [[nodiscard]] const GeneratedMultiAccount& getImportedAccount() const override;
/// \see ServiceInterface /// \see ServiceInterface
[[nodiscard]] bool isFirstTimeAccountLogin() const override; [[nodiscard]] bool isFirstTimeAccountLogin() const override;
@ -44,34 +46,34 @@ public:
/// \see ServiceInterface /// \see ServiceInterface
bool setKeyStoreDir(const QString &key) override; bool setKeyStoreDir(const QString &key) override;
QString login(AccountDto account, const QString& password) override; QString login(MultiAccount account, const QString& password) override;
void clear() override; void clear() override;
QString generateAlias(const QString& publicKey) override; QString generateAlias(const QString& publicKey) override;
void deleteMultiAccount(const AccountDto &account) override; void deleteMultiAccount(const MultiAccount &account) override;
private: private:
QJsonObject prepareAccountJsonObject(const GeneratedAccountDto& account, const QString& displayName) const; QJsonObject prepareAccountJsonObject(const GeneratedMultiAccount& account, const QString& displayName) const;
DerivedAccounts storeDerivedAccounts(const QString& accountId, const QString& hashedPassword, DerivedAccounts storeDerivedAccounts(const QString& accountId, const StatusGo::HashedPassword& password,
const QVector<QString>& paths); const std::vector<Accounts::DerivationPath>& paths);
StoredAccountDto storeAccount(const QString& accountId, const QString& hashedPassword); StoredMultiAccount storeAccount(const QString& accountId, const StatusGo::HashedPassword& password);
AccountDto saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, MultiAccount saveAccountAndLogin(const StatusGo::HashedPassword& password, const QJsonObject& account,
const QJsonArray& subaccounts, const QJsonObject& settings, const QJsonArray& subaccounts, const QJsonObject& settings,
const QJsonObject& config); const QJsonObject& config);
QJsonObject getAccountDataForAccountId(const QString& accountId, const QString& displayName) const; QJsonObject getAccountDataForAccountId(const QString& accountId, const QString& displayName) const;
QJsonArray prepareSubaccountJsonObject(const GeneratedAccountDto& account, const QString& displayName) const; QJsonArray prepareSubaccountJsonObject(const GeneratedMultiAccount& account, const QString& displayName) const;
QJsonArray getSubaccountDataForAccountId(const QString& accountId, const QString& displayName) const; QJsonArray getSubaccountDataForAccountId(const QString& accountId, const QString& displayName) const;
QString generateSigningPhrase(int count) const; QString generateSigningPhrase(int count) const;
QJsonObject prepareAccountSettingsJsonObject(const GeneratedAccountDto& account, QJsonObject prepareAccountSettingsJsonObject(const GeneratedMultiAccount& account,
const QString& installationId, const QString& installationId,
const QString& displayName) const; const QString& displayName) const;
@ -80,14 +82,14 @@ private:
QJsonObject getDefaultNodeConfig(const QString& installationId) const; QJsonObject getDefaultNodeConfig(const QString& installationId) const;
private: private:
std::vector<GeneratedAccountDto> m_generatedAccounts; std::vector<GeneratedMultiAccount> m_generatedAccounts;
fs::path m_statusgoDataDir; fs::path m_statusgoDataDir;
fs::path m_keyStoreDir; fs::path m_keyStoreDir;
bool m_isFirstTimeAccountLogin; bool m_isFirstTimeAccountLogin;
// TODO: don't see the need for this state here // TODO: don't see the need for this state here
AccountDto m_loggedInAccount; MultiAccount m_loggedInAccount;
GeneratedAccountDto m_importedAccount; GeneratedMultiAccount m_importedAccount;
// Here for now. Extract them if used by other services // Here for now. Extract them if used by other services
static constexpr auto m_keyStoreDirName = "keystore"; static constexpr auto m_keyStoreDirName = "keystore";

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "AccountDto.h" #include "MultiAccount.h"
#include "GeneratedAccountDto.h" #include "GeneratedMultiAccount.h"
#include <filesystem> #include <filesystem>
@ -20,18 +20,18 @@ public:
virtual bool init(const fs::path& statusgoDataDir) = 0; virtual bool init(const fs::path& statusgoDataDir) = 0;
/// opens database and returns accounts list. /// opens database and returns accounts list.
[[nodiscard]] virtual std::vector<AccountDto> openAndListAccounts() = 0; [[nodiscard]] virtual std::vector<MultiAccount> openAndListAccounts() = 0;
/// Retrieve cached accounts generated in \c init /// Retrieve cached accounts generated in \c init
[[nodiscard]] virtual const std::vector<GeneratedAccountDto>& generatedAccounts() const = 0; [[nodiscard]] virtual const std::vector<GeneratedMultiAccount>& generatedAccounts() const = 0;
/// Configure an generated account. \a accountID must be sourced from \c generatedAccounts /// 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 /// Account that is currently logged-in
[[nodiscard]] virtual const AccountDto& getLoggedInAccount() const = 0; [[nodiscard]] virtual const MultiAccount& getLoggedInAccount() const = 0;
[[nodiscard]] virtual const GeneratedAccountDto& getImportedAccount() const = 0; [[nodiscard]] virtual const GeneratedMultiAccount& getImportedAccount() const = 0;
/// Check if the login was never done in the current \c data directory /// Check if the login was never done in the current \c data directory
[[nodiscard]] virtual bool isFirstTimeAccountLogin() const = 0; [[nodiscard]] virtual bool isFirstTimeAccountLogin() const = 0;
@ -39,13 +39,13 @@ public:
/// Set and initializes the keystore directory. \see StatusGo::General::initKeystore /// 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(AccountDto account, const QString& password) = 0; virtual QString login(MultiAccount account, const QString& password) = 0;
virtual void clear() = 0; virtual void clear() = 0;
virtual QString generateAlias(const QString& publicKey) = 0; virtual QString generateAlias(const QString& publicKey) = 0;
virtual void deleteMultiAccount(const AccountDto &account) = 0; virtual void deleteMultiAccount(const MultiAccount &account) = 0;
}; };
using AccountsServiceInterfacePtr = std::shared_ptr<AccountsServiceInterface>; using AccountsServiceInterfacePtr = std::shared_ptr<AccountsServiceInterface>;

View File

@ -50,19 +50,19 @@ struct DerivedAccounts
for(const auto &derivationPath : jsonObj.keys()) for(const auto &derivationPath : jsonObj.keys())
{ {
auto derivedObj = jsonObj.value(derivationPath).toObject(); auto derivedObj = jsonObj.value(derivationPath).toObject();
if(derivationPath == Constants::General::PathWhisper) if(derivationPath == Constants::General::PathWhisper.get())
{ {
result.whisper = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); result.whisper = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath);
} }
else if(derivationPath == Constants::General::PathWalletRoot) else if(derivationPath == Constants::General::PathWalletRoot.get())
{ {
result.walletRoot = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); result.walletRoot = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath);
} }
else if(derivationPath == Constants::General::PathDefaultWallet) else if(derivationPath == Constants::General::PathDefaultWallet.get())
{ {
result.defaultWallet = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); result.defaultWallet = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath);
} }
else if(derivationPath == Constants::General::PathEIP1581) else if(derivationPath == Constants::General::PathEIP1581.get())
{ {
result.eip1581 = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath); result.eip1581 = DerivedAccountDetails::toDerivedAccountDetails(derivedObj, derivationPath);
} }
@ -72,28 +72,28 @@ struct DerivedAccounts
} }
}; };
struct StoredAccountDto struct StoredMultiAccount
{ {
QString publicKey; QString publicKey;
QString address; QString address;
}; };
static StoredAccountDto toStoredAccountDto(const QJsonObject& jsonObj) static StoredMultiAccount toStoredMultiAccount(const QJsonObject& jsonObj)
{ {
auto result = StoredAccountDto(); auto result = StoredMultiAccount();
try { try {
result.address = Json::getMandatoryProp(jsonObj, "address")->toString(); result.address = Json::getMandatoryProp(jsonObj, "address")->toString();
result.publicKey = Json::getMandatoryProp(jsonObj, "publicKey")->toString(); result.publicKey = Json::getMandatoryProp(jsonObj, "publicKey")->toString();
} catch (std::exception e) { } catch (std::exception e) {
qWarning() << QString("Mapping StoredAccountDto failed: %1").arg(e.what()); qWarning() << QString("Mapping StoredMultiAccount failed: %1").arg(e.what());
} }
return result; return result;
} }
struct GeneratedAccountDto struct GeneratedMultiAccount
{ {
QString id; QString id;
QString publicKey; QString publicKey;
@ -110,9 +110,9 @@ struct GeneratedAccountDto
return !(id.isEmpty() || publicKey.isEmpty() || address.isEmpty() || keyUid.isEmpty()); return !(id.isEmpty() || publicKey.isEmpty() || address.isEmpty() || keyUid.isEmpty());
} }
static GeneratedAccountDto toGeneratedAccountDto(const QJsonObject& jsonObj) static GeneratedMultiAccount toGeneratedMultiAccount(const QJsonObject& jsonObj)
{ {
auto result = GeneratedAccountDto(); auto result = GeneratedMultiAccount();
try try
{ {
@ -130,7 +130,7 @@ struct GeneratedAccountDto
} }
catch (std::exception e) catch (std::exception e)
{ {
qWarning() << QString("Mapping GeneratedAccountDto failed: %1").arg(e.what()); qWarning() << QString("Mapping GeneratedMultiAccount failed: %1").arg(e.what());
} }
return result; return result;

View File

@ -4,14 +4,18 @@
#include "Common/SigningPhrases.h" #include "Common/SigningPhrases.h"
#include "Common/Json.h" #include "Common/Json.h"
#include <StatusGo/Accounts/accounts_types.h>
#include <QtCore> #include <QtCore>
namespace Accounts = Status::StatusGo::Accounts;
// TODO: Move to StatusGo library // TODO: Move to StatusGo library
namespace Status::Onboarding namespace Status::Onboarding
{ {
// TODO: refactor it to MultiAccount /// \note equivalent of status-go's multiaccounts.Account@multiaccounts/database.go
struct AccountDto struct MultiAccount
{ {
QString name; QString name;
long timestamp; long timestamp;
@ -20,16 +24,16 @@ struct AccountDto
// TODO images // TODO images
// TODO colorHash // TODO colorHash
// TODO colorId // TODO colorId
QString address; Accounts::EOAddress address;
bool isValid() const bool isValid() const
{ {
return !(name.isEmpty() || keyUid.isEmpty()); return !(name.isEmpty() || keyUid.isEmpty());
} }
static AccountDto toAccountDto(const QJsonObject& jsonObj) static MultiAccount toMultiAccount(const QJsonObject& jsonObj)
{ {
auto result = AccountDto(); auto result = MultiAccount();
try try
{ {
@ -43,13 +47,13 @@ struct AccountDto
} }
result.keycardPairing = Json::getMandatoryProp(jsonObj, "keycard-pairing")->toString(); result.keycardPairing = Json::getMandatoryProp(jsonObj, "keycard-pairing")->toString();
result.keyUid = Json::getMandatoryProp(jsonObj, "key-uid")->toString(); result.keyUid = Json::getMandatoryProp(jsonObj, "key-uid")->toString();
result.address = Json::getProp(jsonObj, "address")->toString(); result.address = Accounts::EOAddress(Json::getProp(jsonObj, "address")->toString());
/// TODO: investigate unhandled `photo-path` value /// TODO: investigate unhandled `photo-path` value
} }
catch (std::exception e) catch (std::exception e)
{ {
qWarning() << QObject::tr("Mapping AccountDto failed: %1").arg(e.what()); qWarning() << QString("Mapping MultiAccount failed: %1").arg(e.what());
} }
return result; return result;

View File

@ -6,8 +6,8 @@ target_sources(${PROJECT_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/UserAccount.h ${CMAKE_CURRENT_SOURCE_DIR}/UserAccount.h
${CMAKE_CURRENT_SOURCE_DIR}/UserAccountsModel.h ${CMAKE_CURRENT_SOURCE_DIR}/UserAccountsModel.h
${CMAKE_CURRENT_SOURCE_DIR}/Accounts/AccountDto.h ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/MultiAccount.h
${CMAKE_CURRENT_SOURCE_DIR}/Accounts/GeneratedAccountDto.h ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/GeneratedMultiAccount.h
${CMAKE_CURRENT_SOURCE_DIR}/Accounts/AccountsService.h ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/AccountsService.h
${CMAKE_CURRENT_SOURCE_DIR}/Accounts/AccountsServiceInterface.h ${CMAKE_CURRENT_SOURCE_DIR}/Accounts/AccountsServiceInterface.h

View File

@ -1,8 +1,12 @@
#pragma once #pragma once
#include <StatusGo/Accounts/accounts_types.h>
#include <QtCore> #include <QtCore>
#include <QStringLiteral> #include <QStringLiteral>
namespace Accounts = Status::StatusGo::Accounts;
namespace Status::Constants namespace Status::Constants
{ {
@ -34,15 +38,15 @@ namespace General
inline const auto ZeroAddress = u"0x0000000000000000000000000000000000000000"_qs; inline const auto ZeroAddress = u"0x0000000000000000000000000000000000000000"_qs;
inline const auto PathWalletRoot = u"m/44'/60'/0'/0"_qs; inline const Accounts::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 // EIP1581 Root Key, the extended key from which any whisper key/encryption key can be derived
inline const auto PathEIP1581 = u"m/43'/60'/1581'"_qs; inline const Accounts::DerivationPath PathEIP1581{u"m/43'/60'/1581'"_qs};
// BIP44-0 Wallet key, the default wallet key // BIP44-0 Wallet key, the default wallet key
inline const auto PathDefaultWallet = PathWalletRoot + u"/0"_qs; inline const Accounts::DerivationPath PathDefaultWallet{PathWalletRoot.get() + u"/0"_qs};
// EIP1581 Chat Key 0, the default whisper key // EIP1581 Chat Key 0, the default whisper key
inline const auto PathWhisper = PathEIP1581 + u"/0'/0"_qs; inline const Accounts::DerivationPath PathWhisper{PathEIP1581.get() + u"/0'/0"_qs};
inline const QVector<QString> AccountDefaultPaths {PathWalletRoot, PathEIP1581, PathWhisper, PathDefaultWallet}; inline const std::vector<Accounts::DerivationPath> AccountDefaultPaths {PathWalletRoot, PathEIP1581, PathWhisper, PathDefaultWallet};
} }
} }

View File

@ -3,7 +3,7 @@
#include "UserAccountsModel.h" #include "UserAccountsModel.h"
#include "Accounts/AccountsServiceInterface.h" #include "Accounts/AccountsServiceInterface.h"
#include "Accounts/AccountDto.h" #include "Accounts/MultiAccount.h"
#include <QtQmlIntegration> #include <QtQmlIntegration>

View File

@ -17,7 +17,7 @@ OnboardingController::OnboardingController(AccountsServiceInterfacePtr accountsS
{ // Init accounts { // Init accounts
std::vector<std::shared_ptr<UserAccount>> accounts; std::vector<std::shared_ptr<UserAccount>> accounts;
for(auto &account : getOpenedAccounts()) { for(auto &account : getOpenedAccounts()) {
accounts.push_back(std::make_shared<UserAccount>(std::make_unique<AccountDto>(std::move(account)))); accounts.push_back(std::make_shared<UserAccount>(std::make_unique<MultiAccount>(std::move(account))));
} }
m_accounts = std::make_shared<UserAccountsModel>(std::move(accounts)); m_accounts = std::make_shared<UserAccountsModel>(std::move(accounts));
} }
@ -38,7 +38,7 @@ void OnboardingController::onLogin(const QString& error)
emit accountLoginError(error); emit accountLoginError(error);
} }
std::vector<AccountDto> OnboardingController::getOpenedAccounts() const std::vector<MultiAccount> OnboardingController::getOpenedAccounts() const
{ {
return m_accountsService->openAndListAccounts(); return m_accountsService->openAndListAccounts();
} }

View File

@ -2,7 +2,7 @@
#include "UserAccountsModel.h" #include "UserAccountsModel.h"
#include "Accounts/AccountDto.h" #include "Accounts/MultiAccount.h"
#include <QQmlEngine> #include <QQmlEngine>
#include <QtQmlIntegration> #include <QtQmlIntegration>
@ -38,7 +38,7 @@ public:
~OnboardingController(); ~OnboardingController();
/// Retrieve available accounts /// Retrieve available accounts
std::vector<AccountDto> getOpenedAccounts() const; std::vector<MultiAccount> getOpenedAccounts() const;
/// Login user account /// Login user account
/// TODO: \a user should be of type \c UserAccount but this doesn't work with Qt6 CMake API. Investigate and fix later on /// TODO: \a user should be of type \c UserAccount but this doesn't work with Qt6 CMake API. Investigate and fix later on

View File

@ -1,12 +1,11 @@
#include "UserAccount.h" #include "UserAccount.h"
#include "Accounts/AccountDto.h" #include "Accounts/MultiAccount.h"
namespace Status::Onboarding namespace Status::Onboarding
{ {
UserAccount::UserAccount(std::unique_ptr<AccountDto> data) UserAccount::UserAccount(std::unique_ptr<MultiAccount> data)
: QObject() : QObject()
, m_data(std::move(data)) , m_data(std::move(data))
{ {
@ -18,12 +17,12 @@ const QString &UserAccount::name() const
return m_data->name; return m_data->name;
} }
const AccountDto &UserAccount::accountData() const const MultiAccount &UserAccount::accountData() const
{ {
return *m_data; return *m_data;
} }
void UserAccount::updateAccountData(const AccountDto& newData) void UserAccount::updateAccountData(const MultiAccount& newData)
{ {
std::vector<std::function<void()>> notifyUpdates; std::vector<std::function<void()>> notifyUpdates;

View File

@ -5,7 +5,7 @@
namespace Status::Onboarding namespace Status::Onboarding
{ {
class AccountDto; class MultiAccount;
/*! /*!
* \brief Represents a user account in Onboarding Presentation Layer * \brief Represents a user account in Onboarding Presentation Layer
@ -21,18 +21,18 @@ class UserAccount: public QObject
Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString name READ name NOTIFY nameChanged)
public: public:
explicit UserAccount(std::unique_ptr<AccountDto> data); explicit UserAccount(std::unique_ptr<MultiAccount> data);
const QString &name() const; const QString &name() const;
const AccountDto& accountData() const; const MultiAccount& accountData() const;
void updateAccountData(const AccountDto& newData); void updateAccountData(const MultiAccount& newData);
signals: signals:
void nameChanged(); void nameChanged();
private: private:
std::unique_ptr<AccountDto> m_data; std::unique_ptr<MultiAccount> m_data;
}; };
} }

View File

@ -41,12 +41,8 @@ target_link_libraries(${PROJECT_NAME}
Status::OnboardingTestHelpers Status::OnboardingTestHelpers
Status::Onboarding Status::Onboarding
# TODO tmp
Status::StatusGoQt
) )
include(GoogleTest) include(GoogleTest)
gtest_add_tests( gtest_add_tests(
TARGET ${PROJECT_NAME} TARGET ${PROJECT_NAME}

View File

@ -110,7 +110,7 @@ void ScopedTestAccount::logOut()
throw std::runtime_error("ScopedTestAccount - failed logging out"); throw std::runtime_error("ScopedTestAccount - failed logging out");
} }
Accounts::MultiAccount ScopedTestAccount::firstChatAccount() Accounts::ChatOrWalletAccount ScopedTestAccount::firstChatAccount()
{ {
auto accounts = Accounts::getAccounts(); auto accounts = Accounts::getAccounts();
auto chatIt = std::find_if(accounts.begin(), accounts.end(), [](const auto& a) { auto chatIt = std::find_if(accounts.begin(), accounts.end(), [](const auto& a) {
@ -121,7 +121,7 @@ Accounts::MultiAccount ScopedTestAccount::firstChatAccount()
return *chatIt; return *chatIt;
} }
Accounts::MultiAccount ScopedTestAccount::firstWalletAccount() Accounts::ChatOrWalletAccount ScopedTestAccount::firstWalletAccount()
{ {
auto accounts = Accounts::getAccounts(); auto accounts = Accounts::getAccounts();
auto walletIt = std::find_if(accounts.begin(), accounts.end(), [](const auto& a) { auto walletIt = std::find_if(accounts.begin(), accounts.end(), [](const auto& a) {

View File

@ -36,8 +36,8 @@ public:
void processMessages(size_t millis, std::function<bool()> shouldWaitUntilTimeout); void processMessages(size_t millis, std::function<bool()> shouldWaitUntilTimeout);
void logOut(); void logOut();
static Accounts::MultiAccount firstChatAccount(); static Accounts::ChatOrWalletAccount firstChatAccount();
static Accounts::MultiAccount firstWalletAccount(); static Accounts::ChatOrWalletAccount firstWalletAccount();
QString password() const { return m_accountPassword; }; QString password() const { return m_accountPassword; };

View File

@ -21,17 +21,17 @@ public:
virtual ~AccountsServiceMock() override {}; virtual ~AccountsServiceMock() override {};
MOCK_METHOD(bool, init, (const fs::path&), (override)); MOCK_METHOD(bool, init, (const fs::path&), (override));
MOCK_METHOD(std::vector<Onboarding::AccountDto>, openAndListAccounts, (), (override)); MOCK_METHOD(std::vector<Onboarding::MultiAccount>, openAndListAccounts, (), (override));
MOCK_METHOD(const std::vector<Onboarding::GeneratedAccountDto>&, generatedAccounts, (), (const, override)); MOCK_METHOD(const std::vector<Onboarding::GeneratedMultiAccount>&, generatedAccounts, (), (const, override));
MOCK_METHOD(bool, setupAccountAndLogin, (const QString&, const QString&, const QString&), (override)); MOCK_METHOD(bool, setupAccountAndLogin, (const QString&, const QString&, const QString&), (override));
MOCK_METHOD(const Onboarding::AccountDto&, getLoggedInAccount, (), (const, override)); MOCK_METHOD(const Onboarding::MultiAccount&, getLoggedInAccount, (), (const, override));
MOCK_METHOD(const Onboarding::GeneratedAccountDto&, getImportedAccount, (), (const, override)); MOCK_METHOD(const Onboarding::GeneratedMultiAccount&, getImportedAccount, (), (const, override));
MOCK_METHOD(bool, isFirstTimeAccountLogin, (), (const, override)); MOCK_METHOD(bool, isFirstTimeAccountLogin, (), (const, override));
MOCK_METHOD(bool, setKeyStoreDir, (const QString&), (override)); MOCK_METHOD(bool, setKeyStoreDir, (const QString&), (override));
MOCK_METHOD(QString, login, (Onboarding::AccountDto, const QString&), (override)); MOCK_METHOD(QString, login, (Onboarding::MultiAccount, const QString&), (override));
MOCK_METHOD(void, clear, (), (override)); MOCK_METHOD(void, clear, (), (override));
MOCK_METHOD(QString, generateAlias, (const QString&), (override)); MOCK_METHOD(QString, generateAlias, (const QString&), (override));
MOCK_METHOD(void, deleteMultiAccount, (const Onboarding::AccountDto&), (override)); MOCK_METHOD(void, deleteMultiAccount, (const Onboarding::MultiAccount&), (override));
}; };
} }

View File

@ -18,7 +18,6 @@ add_library(${PROJECT_NAME} SHARED)
# Use by linker only # Use by linker only
set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS Debug) set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS Debug)
# TODO: consider adding a private header for parsing and keep json dependency away!?
target_link_libraries(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME}
PUBLIC PUBLIC
Status::Helpers Status::Helpers
@ -76,10 +75,11 @@ target_sources(${PROJECT_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/Accounts.h ${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/Accounts.h
${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/Accounts.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/Accounts.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/accounts_types.h
${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/AccountsAPI.h ${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/AccountsAPI.h
${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/AccountsAPI.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/AccountsAPI.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/MultiAccount.h ${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/ChatOrWalletAccount.h
${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/MultiAccount.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Accounts/ChatOrWalletAccount.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Messenger/Service.h ${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Messenger/Service.h
${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Messenger/Service.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/StatusGo/Messenger/Service.cpp

View File

@ -9,7 +9,7 @@ const int MNEMONIC_PHRASE_LENGTH = 12;
namespace Status::StatusGo::Accounts { namespace Status::StatusGo::Accounts {
RpcResponse<QJsonArray> generateAddresses(const QVector<QString>& paths) RpcResponse<QJsonArray> generateAddresses(const std::vector<Accounts::DerivationPath>& paths)
{ {
QJsonObject payload{ QJsonObject payload{
{"n", NUMBER_OF_ADDRESSES_TO_GENERATE}, {"n", NUMBER_OF_ADDRESSES_TO_GENERATE},
@ -64,13 +64,12 @@ RpcResponse<QString> generateAlias(const QString& publicKey)
} }
} }
RpcResponse<QJsonObject> storeDerivedAccounts(const QString& id, const QString& hashedPassword, RpcResponse<QJsonObject> storeDerivedAccounts(const QString& id, const HashedPassword& password, const std::vector<Accounts::DerivationPath>& paths)
const QVector<QString>& paths)
{ {
QJsonObject payload{ QJsonObject payload{
{"accountID", id}, {"accountID", id},
{"paths", Utils::toJsonArray(paths)}, {"paths", Utils::toJsonArray(paths)},
{"password", hashedPassword} {"password", password.get()}
}; };
try try
@ -101,11 +100,11 @@ RpcResponse<QJsonObject> storeDerivedAccounts(const QString& id, const QString&
} }
} }
RpcResponse<QJsonObject> storeAccount(const QString& id, const QString& hashedPassword) RpcResponse<QJsonObject> storeAccount(const QString& id, const HashedPassword& password)
{ {
QJsonObject payload{ QJsonObject payload{
{"accountID", id}, {"accountID", id},
{"password", hashedPassword} {"password", password.get()}
}; };
try try
@ -136,14 +135,14 @@ RpcResponse<QJsonObject> storeAccount(const QString& id, const QString& hashedPa
} }
} }
bool saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, bool saveAccountAndLogin(const HashedPassword& password, const QJsonObject& account,
const QJsonArray& subaccounts, const QJsonObject& settings, const QJsonArray& subaccounts, const QJsonObject& settings,
const QJsonObject& nodeConfig) const QJsonObject& nodeConfig)
{ {
try try
{ {
auto result = SaveAccountAndLogin(Utils::jsonToByteArray(account).data(), auto result = SaveAccountAndLogin(Utils::jsonToByteArray(account).data(),
hashedPassword.toUtf8().data(), password.get().toUtf8().data(),
Utils::jsonToByteArray(settings).data(), Utils::jsonToByteArray(settings).data(),
Utils::jsonToByteArray(nodeConfig).data(), Utils::jsonToByteArray(nodeConfig).data(),
Utils::jsonToByteArray(subaccounts).data()); Utils::jsonToByteArray(subaccounts).data());
@ -193,7 +192,7 @@ RpcResponse<QJsonArray> openAccounts(const char* dataDirPath)
} }
} }
RpcResponse<QJsonObject> login(const QString& name, const QString& keyUid, const QString& hashedPassword, RpcResponse<QJsonObject> login(const QString& name, const QString& keyUid, const HashedPassword& password,
const QString& thumbnail, const QString& large) const QString& thumbnail, const QString& large)
{ {
QJsonObject payload{ QJsonObject payload{
@ -210,7 +209,7 @@ RpcResponse<QJsonObject> login(const QString& name, const QString& keyUid, const
try try
{ {
auto payloadData = Utils::jsonToByteArray(std::move(payload)); auto payloadData = Utils::jsonToByteArray(std::move(payload));
auto result = Login(payloadData.data(), hashedPassword.toUtf8().data()); auto result = Login(payloadData.data(), password.get().toUtf8().data());
QJsonObject jsonResult; QJsonObject jsonResult;
if(!Utils::checkReceivedResponse(result, jsonResult)) if(!Utils::checkReceivedResponse(result, jsonResult))
{ {
@ -234,7 +233,7 @@ RpcResponse<QJsonObject> login(const QString& name, const QString& keyUid, const
} }
} }
RpcResponse<QJsonObject> loginWithConfig(const QString& name, const QString& keyUid, const QString& hashedPassword, RpcResponse<QJsonObject> loginWithConfig(const QString& name, const QString& keyUid, const HashedPassword& password,
const QString& thumbnail, const QString& large, const QJsonObject& nodeConfig) const QString& thumbnail, const QString& large, const QJsonObject& nodeConfig)
{ {
QJsonObject payload{ QJsonObject payload{
@ -252,7 +251,7 @@ RpcResponse<QJsonObject> loginWithConfig(const QString& name, const QString& key
{ {
auto payloadData = Utils::jsonToByteArray(std::move(payload)); auto payloadData = Utils::jsonToByteArray(std::move(payload));
auto nodeConfigData = Utils::jsonToByteArray(nodeConfig); auto nodeConfigData = Utils::jsonToByteArray(nodeConfig);
auto result = LoginWithConfig(payloadData.data(), hashedPassword.toUtf8().data(), nodeConfigData.data()); auto result = LoginWithConfig(payloadData.data(), password.get().toUtf8().data(), nodeConfigData.data());
QJsonObject jsonResult; QJsonObject jsonResult;
if(!Utils::checkReceivedResponse(result, jsonResult)) if(!Utils::checkReceivedResponse(result, jsonResult))
{ {

View File

@ -1,31 +1,31 @@
#pragma once #pragma once
#include "Types.h" #include "Types.h"
#include "accounts_types.h"
#include <QtCore> #include <QtCore>
namespace Status::StatusGo::Accounts namespace Status::StatusGo::Accounts
{ {
RpcResponse<QJsonArray> generateAddresses(const QVector<QString>& 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 QString& hashedPassword, RpcResponse<QJsonObject> storeDerivedAccounts(const QString& accountId, const HashedPassword& password,
const QVector<QString>& paths); const std::vector<Accounts::DerivationPath>& paths);
RpcResponse<QJsonObject> storeAccount(const QString& id, const QString& hashedPassword); RpcResponse<QJsonObject> storeAccount(const QString& id, const HashedPassword& password);
bool saveAccountAndLogin(const QString& hashedPassword, const QJsonObject& account, bool saveAccountAndLogin(const StatusGo::HashedPassword& password, const QJsonObject& account,
const QJsonArray& subaccounts, const QJsonObject& settings, const QJsonArray& subaccounts, const QJsonObject& settings,
const QJsonObject& nodeConfig); const QJsonObject& nodeConfig);
/// opens database and returns accounts list. /// opens database and returns accounts list.
RpcResponse<QJsonArray> openAccounts(const char* dataDirPath); RpcResponse<QJsonArray> openAccounts(const char* dataDirPath);
/// TODO harmonise password parameters (hashed or plain)? RpcResponse<QJsonObject> login(const QString& name, const QString& keyUid, const HashedPassword& password,
RpcResponse<QJsonObject> login(const QString& name, const QString& keyUid, const QString& hashedPassword,
const QString& thumbnail, const QString& large); const QString& thumbnail, const QString& large);
RpcResponse<QJsonObject> loginWithConfig(const QString& name, const QString& keyUid, const QString& hashedPassword, RpcResponse<QJsonObject> loginWithConfig(const QString& name, const QString& keyUid, const HashedPassword& password,
const QString& thumbnail, const QString& large, const QJsonObject& nodeConfig); const QString& thumbnail, const QString& large, const QJsonObject& nodeConfig);
RpcResponse<QJsonObject> logout(); RpcResponse<QJsonObject> logout();
} }

View File

@ -14,7 +14,7 @@ using json = nlohmann::json;
namespace Status::StatusGo::Accounts namespace Status::StatusGo::Accounts
{ {
Accounts::MultiAccounts getAccounts() { Accounts::ChatOrWalletAccounts getAccounts() {
// or even nicer with a raw string literal // or even nicer with a raw string literal
json inputJson = { json inputJson = {
{"jsonrpc", "2.0"}, {"jsonrpc", "2.0"},
@ -29,10 +29,10 @@ Accounts::MultiAccounts getAccounts() {
return resultJson.get<CallPrivateRpcResponse>().result; return resultJson.get<CallPrivateRpcResponse>().result;
} }
void generateAccountWithDerivedPath(const QString &hashedPassword, const QString &name, const QColor &color, const QString &emoji, void generateAccountWithDerivedPath(const HashedPassword &password, const QString &name, const QColor &color, const QString &emoji,
const QString &path, const QString &derivedFrom) const DerivationPath &path, const EOAddress &derivedFrom)
{ {
std::vector<json> params = {hashedPassword, name, color, emoji, path, derivedFrom}; std::vector<json> params = {password, name, color, emoji, path, derivedFrom};
json inputJson = { json inputJson = {
{"jsonrpc", "2.0"}, {"jsonrpc", "2.0"},
{"method", "accounts_generateAccountWithDerivedPath"}, {"method", "accounts_generateAccountWithDerivedPath"},
@ -44,10 +44,10 @@ void generateAccountWithDerivedPath(const QString &hashedPassword, const QString
checkPrivateRpcCallResultAndReportError(resultJson); checkPrivateRpcCallResultAndReportError(resultJson);
} }
void addAccountWithMnemonicAndPath(const QString &mnemonic, const QString &hashedPassword, const QString &name, void addAccountWithMnemonicAndPath(const QString &mnemonic, const HashedPassword &password, const QString &name,
const QColor &color, const QString &emoji, const QString &path) const QColor &color, const QString &emoji, const DerivationPath &path)
{ {
std::vector<json> params = {mnemonic, hashedPassword, name, color, emoji, path}; std::vector<json> params = {mnemonic, password, name, color, emoji, path};
json inputJson = { json inputJson = {
{"jsonrpc", "2.0"}, {"jsonrpc", "2.0"},
{"method", "accounts_addAccountWithMnemonicAndPath"}, {"method", "accounts_addAccountWithMnemonicAndPath"},
@ -59,7 +59,7 @@ void addAccountWithMnemonicAndPath(const QString &mnemonic, const QString &hashe
checkPrivateRpcCallResultAndReportError(resultJson); checkPrivateRpcCallResultAndReportError(resultJson);
} }
void addAccountWatch(const QString &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}; std::vector<json> params = {address, name, color, emoji};
json inputJson = { json inputJson = {
@ -73,7 +73,7 @@ void addAccountWatch(const QString &address, const QString &name, const QColor &
checkPrivateRpcCallResultAndReportError(resultJson); checkPrivateRpcCallResultAndReportError(resultJson);
} }
void deleteAccount(const QString &address) void deleteAccount(const EOAddress &address)
{ {
std::vector<json> params = {address}; std::vector<json> params = {address};
json inputJson = { json inputJson = {

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include "Types.h"
#include "Accounts/MultiAccount.h" #include "Accounts/ChatOrWalletAccount.h"
#include "Accounts/accounts_types.h"
#include <vector> #include <vector>
#include <filesystem> #include <filesystem>
@ -16,34 +18,34 @@ namespace Status::StatusGo::Accounts
/// \brief Retrieve all available accounts Wallet and Chat /// \brief Retrieve all available accounts Wallet and Chat
/// \note status-go returns accounts in \c CallPrivateRpcResponse.result /// \note status-go returns accounts in \c CallPrivateRpcResponse.result
/// \throws \c CallPrivateRpcError /// \throws \c CallPrivateRpcError
Accounts::MultiAccounts getAccounts(); Accounts::ChatOrWalletAccounts getAccounts();
/// \brief Generate a new account /// \brief Generate a new account
/// \note the underlying status-go api, SaveAccounts@accounts.go, returns `nil` for \c CallPrivateRpcResponse.result /// \note the underlying status-go api, SaveAccounts@accounts.go, returns `nil` for \c CallPrivateRpcResponse.result
/// \see \c getAccounts /// \see \c getAccounts
/// \throws \c CallPrivateRpcError /// \throws \c CallPrivateRpcError
void generateAccountWithDerivedPath(const QString &password, const QString &name, void generateAccountWithDerivedPath(const HashedPassword &password, const QString &name,
const QColor &color, const QString &emoji, const QColor &color, const QString &emoji,
const QString &path, const QString &derivedFrom); const DerivationPath &path, const Accounts::EOAddress &derivedFrom);
/// \brief Add a new account from an existing mnemonic /// \brief Add a new account from an existing mnemonic
/// \note the underlying status-go api, SaveAccounts@accounts.go, returns `nil` for \c CallPrivateRpcResponse.result /// \note the underlying status-go api, SaveAccounts@accounts.go, returns `nil` for \c CallPrivateRpcResponse.result
/// \see \c getAccounts /// \see \c getAccounts
/// \throws \c CallPrivateRpcError /// \throws \c CallPrivateRpcError
void addAccountWithMnemonicAndPath(const QString &mnemonic, const QString &hashedPassword, const QString &name, void addAccountWithMnemonicAndPath(const QString &mnemonic, const HashedPassword &password, const QString &name,
const QColor &color, const QString &emoji, const QString &path); const QColor &color, const QString &emoji, const DerivationPath &path);
/// \brief Add a watch only account /// \brief Add a watch only account
/// \note the underlying status-go api, SaveAccounts@accounts.go, returns `nil` for \c CallPrivateRpcResponse.result /// \note the underlying status-go api, SaveAccounts@accounts.go, returns `nil` for \c CallPrivateRpcResponse.result
/// \see \c getAccounts /// \see \c getAccounts
/// \throws \c CallPrivateRpcError /// \throws \c CallPrivateRpcError
void addAccountWatch(const QString &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 /// \brief Delete an existing account
/// \note the underlying status-go api, DeleteAccount@accounts.go, returns `os.Remove(keyFile)` /// \note the underlying status-go api, DeleteAccount@accounts.go, returns `os.Remove(keyFile)`
/// \see \c getAccounts /// \see \c getAccounts
/// \throws \c CallPrivateRpcError /// \throws \c CallPrivateRpcError
void deleteAccount(const QString &address); void deleteAccount(const EOAddress &address);
/// \brief Delete an existing account /// \brief Delete an existing account
/// \note the underlying status-go api, DeleteAccount@accounts.go, returns `os.Remove(keyFile)` /// \note the underlying status-go api, DeleteAccount@accounts.go, returns `os.Remove(keyFile)`

View File

@ -1,8 +1,8 @@
#include "MultiAccount.h" #include "ChatOrWalletAccount.h"
namespace Status::StatusGo::Accounts { namespace Status::StatusGo::Accounts {
void to_json(json& j, const MultiAccount& d) { void to_json(json& j, const ChatOrWalletAccount& d) {
j = {{"address", d.address}, j = {{"address", d.address},
{"chat", d.isChat}, {"chat", d.isChat},
{"clock", d.clock}, {"clock", d.clock},
@ -21,7 +21,7 @@ void to_json(json& j, const MultiAccount& d) {
j["derived-from"] = d.derivedFrom.value(); j["derived-from"] = d.derivedFrom.value();
} }
void from_json(const json& j, MultiAccount& d) { void from_json(const json& j, ChatOrWalletAccount& d) {
j.at("address").get_to(d.address); j.at("address").get_to(d.address);
j.at("chat").get_to(d.isChat); j.at("chat").get_to(d.isChat);
j.at("clock").get_to(d.clock); j.at("clock").get_to(d.clock);
@ -40,7 +40,7 @@ void from_json(const json& j, MultiAccount& d) {
if(j.contains(publicKeyKey)) if(j.contains(publicKeyKey))
j.at(publicKeyKey).get_to(d.publicKey); j.at(publicKeyKey).get_to(d.publicKey);
if(d.isWallet && !j.at("derived-from").get<std::string>().empty()) if(d.isWallet && !j.at("derived-from").get<std::string>().empty())
d.derivedFrom = j.at("derived-from").get<QString>(); d.derivedFrom = j.at("derived-from").get<std::optional<EOAddress>>();
} }
} }

View File

@ -0,0 +1,42 @@
#pragma once
#include "accounts_types.h"
#include <Helpers/conversions.h>
#include <QColor>
#include <nlohmann/json.hpp>
#include <vector>
using json = nlohmann::json;
namespace Status::StatusGo::Accounts {
/// \brief Unique wallet account entity
/// \note equivalent of status-go's accounts.Account@multiaccounts/accounts/database.go
struct ChatOrWalletAccount
{
EOAddress address;
bool isChat = false;
int clock = -1;
QColor color;
std::optional<EOAddress> derivedFrom;
QString emoji;
bool isHidden = false;
QString mixedcaseAddress;
QString name;
DerivationPath path;
QString publicKey;
bool isRemoved = false;
bool isWallet = false;
};
using ChatOrWalletAccounts = std::vector<ChatOrWalletAccount>;
void to_json(json& j, const ChatOrWalletAccount& d);
void from_json(const json& j, ChatOrWalletAccount& d);
}

View File

@ -1,42 +0,0 @@
#pragma once
#include <Helpers/conversions.h>
#include <QColor>
#include <nlohmann/json.hpp>
#include <vector>
using json = nlohmann::json;
namespace Status::StatusGo::Accounts {
// TODO: rename to MixedAccount
// TODO: create custom types or just named types for all. Also fix APIs after this
/*! \brief Unique wallet account entity
*/
struct MultiAccount
{
QString address;
bool isChat = false;
int clock = -1;
QColor color;
std::optional<QString> derivedFrom;
QString emoji;
bool isHidden = false;
QString mixedcaseAddress;
QString name;
QString path;
QString publicKey;
bool isRemoved = false;
bool isWallet = false;
};
using MultiAccounts = std::vector<MultiAccount>;
void to_json(json& j, const MultiAccount& d);
void from_json(const json& j, MultiAccount& d);
}

View File

@ -0,0 +1,16 @@
#include <Helpers/NamedType.h>
#include <nlohmann/json.hpp>
#include <QString>
using json = nlohmann::json;
/// Defines phantom types for strong typing
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>;
}

View File

@ -1,10 +1,14 @@
#pragma once #pragma once
#include <Helpers/NamedType.h>
#include <QString> #include <QString>
namespace Status::StatusGo namespace Status::StatusGo
{ {
using HashedPassword = Helpers::NamedType<QString, struct HashedPasswordTag>;
// Used in calls where we don't have version and id returned from `status-go` // Used in calls where we don't have version and id returned from `status-go`
struct RpcError struct RpcError

View File

@ -7,11 +7,11 @@
namespace Status::StatusGo::Utils namespace Status::StatusGo::Utils
{ {
QJsonArray toJsonArray(const QVector<QString>& value) QJsonArray toJsonArray(const std::vector<Accounts::DerivationPath>& value)
{ {
QJsonArray array; QJsonArray array;
for(auto& v : value) for(auto& v : value)
array << v; array << v.get();
return array; return array;
} }
@ -20,10 +20,10 @@ const char* statusGoCallPrivateRPC(const char* inputJSON) {
return CallPrivateRPC(const_cast<char*>(inputJSON)); return CallPrivateRPC(const_cast<char*>(inputJSON));
} }
QString hashString(const QString &str) HashedPassword hashPassword(const QString &str)
{ {
return "0x" + QString::fromUtf8(QCryptographicHash::hash(str.toUtf8(), return HashedPassword("0x" + QString::fromUtf8(QCryptographicHash::hash(str.toUtf8(),
QCryptographicHash::Keccak_256).toHex()); QCryptographicHash::Keccak_256).toHex()));
} }
std::optional<RpcError> getRPCErrorInJson(const QJsonObject& json) std::optional<RpcError> getRPCErrorInJson(const QJsonObject& json)

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "Types.h" #include "Types.h"
#include "Accounts/accounts_types.h"
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
@ -26,7 +27,7 @@ QByteArray jsonToByteArray(const T& json)
return QJsonDocument(json).toJson(QJsonDocument::Compact); return QJsonDocument(json).toJson(QJsonDocument::Compact);
} }
QJsonArray toJsonArray(const QVector<QString>& value); QJsonArray toJsonArray(const std::vector<Accounts::DerivationPath>& value);
/// Check if json contains a standard status-go error and /// Check if json contains a standard status-go error and
std::optional<RpcError> getRPCErrorInJson(const QJsonObject& json); std::optional<RpcError> getRPCErrorInJson(const QJsonObject& json);
@ -111,6 +112,6 @@ RpcResponse<T> callPrivateRpc(const QByteArray& payload)
} }
} }
QString hashString(const QString &str); HashedPassword hashPassword(const QString &str);
} }

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "Accounts/accounts_types.h"
#include <Helpers/conversions.h> #include <Helpers/conversions.h>
#include <QColor> #include <QColor>
@ -8,6 +10,8 @@
#include <vector> #include <vector>
namespace Accounts = Status::StatusGo::Accounts;
using json = nlohmann::json; using json = nlohmann::json;
namespace Status::StatusGo::Wallet { namespace Status::StatusGo::Wallet {
@ -19,10 +23,8 @@ namespace Status::StatusGo::Wallet {
*/ */
struct DerivedAddress struct DerivedAddress
{ {
// TODO create and Address type represents the 20 byte address of an Ethereum account. See https://pkg.go.dev/github.com/ethereum/go-ethereum/common?utm_source=gopls#Address Accounts::EOAddress address;
QString address; Accounts::DerivationPath path;
// TODO: create an Path named type
QString path;
bool hasActivity = false; bool hasActivity = false;
bool alreadyCreated = false; bool alreadyCreated = false;
}; };

View File

@ -3,20 +3,24 @@
#include "Utils.h" #include "Utils.h"
#include "Metadata/api_response.h" #include "Metadata/api_response.h"
#include "Accounts/accounts_types.h"
#include <libstatus.h> #include <libstatus.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <iostream> #include <iostream>
namespace Accounts = Status::StatusGo::Accounts;
using json = nlohmann::json; using json = nlohmann::json;
namespace Status::StatusGo::Wallet namespace Status::StatusGo::Wallet
{ {
DerivedAddresses getDerivedAddressesForPath(const QString &hashedPassword, const QString &derivedFrom, const QString &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 = {hashedPassword, derivedFrom, path, pageSize, pageNumber}; std::vector<json> params = {password, derivedFrom, path, pageSize, pageNumber};
json inputJson = { json inputJson = {
{"jsonrpc", "2.0"}, {"jsonrpc", "2.0"},
{"method", "wallet_getDerivedAddressesForPath"}, {"method", "wallet_getDerivedAddressesForPath"},

View File

@ -1,8 +1,9 @@
#pragma once #pragma once
#include "Accounts/ChatOrWalletAccount.h"
#include "Accounts/MultiAccount.h" #include "Accounts/accounts_types.h"
#include "DerivedAddress.h" #include "DerivedAddress.h"
#include "Types.h"
#include <vector> #include <vector>
@ -13,6 +14,6 @@ namespace Status::StatusGo::Wallet
/// \brief Retrieve a list of derived account addresses /// \brief Retrieve a list of derived account addresses
/// \see \c generateAccountWithDerivedPath /// \see \c generateAccountWithDerivedPath
/// \throws \c CallPrivateRpcError /// \throws \c CallPrivateRpcError
DerivedAddresses getDerivedAddressesForPath(const QString &password, const QString &derivedFrom, const QString &path, int pageSize, int pageNumber); DerivedAddresses getDerivedAddressesForPath(const HashedPassword &password, const Accounts::EOAddress &derivedFrom, const Accounts::DerivationPath &path, int pageSize, int pageNumber);
} // namespaces } // namespaces

View File

@ -13,7 +13,6 @@ qt6_add_qml_module(${PROJECT_NAME}
URI Status URI Status
VERSION 1.0 VERSION 1.0
# TODO: temporary until we make qt_target_qml_sources work
QML_FILES QML_FILES
# Required to suppress "qmllint may not work" warning # Required to suppress "qmllint may not work" warning

View File

@ -10,7 +10,6 @@ qt6_add_qml_module(${PROJECT_NAME}
URI Status.Containers URI Status.Containers
VERSION 1.0 VERSION 1.0
# TODO: temporary until we make qt_target_qml_sources work
QML_FILES QML_FILES
LayoutSpacer.qml LayoutSpacer.qml

View File

@ -10,7 +10,6 @@ qt6_add_qml_module(${PROJECT_NAME}
URI Status.Controls URI Status.Controls
VERSION 1.0 VERSION 1.0
# TODO: temporary until we make qt_target_qml_sources work
QML_FILES QML_FILES
StatusBanner.qml StatusBanner.qml

View File

@ -14,7 +14,6 @@ qt6_add_qml_module(${PROJECT_NAME}
URI Status.Controls.Navigation URI Status.Controls.Navigation
VERSION 1.0 VERSION 1.0
# TODO: temporary until we make qt_target_qml_sources work
QML_FILES QML_FILES
ApplicationContentView.qml ApplicationContentView.qml
ApplicationSection.qml ApplicationSection.qml

View File

@ -10,7 +10,6 @@ qt6_add_qml_module(${PROJECT_NAME}
URI Status.Core URI Status.Core
VERSION 1.0 VERSION 1.0
# TODO: temporary until we make qt_target_qml_sources work
QML_FILES QML_FILES
StatusBaseText.qml StatusBaseText.qml

View File

@ -20,7 +20,6 @@ qt6_add_qml_module(${PROJECT_NAME}
URI Status.Core.Theme URI Status.Core.Theme
VERSION 1.0 VERSION 1.0
# TODO: temporary until we make qt_target_qml_sources work
QML_FILES QML_FILES
StatusColors.qml StatusColors.qml
StatusDarkPalette.qml StatusDarkPalette.qml

View File

@ -12,8 +12,9 @@ qt6_standard_project_setup()
qt6_add_qml_module(${PROJECT_NAME} qt6_add_qml_module(${PROJECT_NAME}
URI Status.TestHelpers URI Status.TestHelpers
VERSION 1.0 VERSION 1.0
# TODO: temporary until we make qt_target_qml_sources work
QML_FILES QML_FILES
# Required to suppress "qmllint may not work" warning # Required to suppress "qmllint may not work" warning
OUTPUT_DIRECTORY OUTPUT_DIRECTORY
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Status/TestHelpers ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Status/TestHelpers

View File

@ -36,14 +36,14 @@ TEST(AccountsAPI, TestGetAccounts)
ASSERT_NE(chatIt, accounts.end()); ASSERT_NE(chatIt, accounts.end());
const auto &chatAccount = *chatIt; const auto &chatAccount = *chatIt;
ASSERT_EQ(chatAccount.name, testAccountName); ASSERT_EQ(chatAccount.name, testAccountName);
ASSERT_FALSE(chatAccount.path.isEmpty()); ASSERT_FALSE(chatAccount.path.get().isEmpty());
ASSERT_FALSE(chatAccount.derivedFrom.has_value()); ASSERT_FALSE(chatAccount.derivedFrom.has_value());
const auto walletIt = std::find_if(accounts.begin(), accounts.end(), [](const auto& a) { return a.isWallet; }); const auto walletIt = std::find_if(accounts.begin(), accounts.end(), [](const auto& a) { return a.isWallet; });
ASSERT_NE(walletIt, accounts.end()); ASSERT_NE(walletIt, accounts.end());
const auto &walletAccount = *walletIt; const auto &walletAccount = *walletIt;
ASSERT_NE(walletAccount.name, testAccountName); ASSERT_NE(walletAccount.name, testAccountName);
ASSERT_FALSE(walletAccount.path.isEmpty()); ASSERT_FALSE(walletAccount.path.get().isEmpty());
ASSERT_TRUE(walletAccount.derivedFrom.has_value()); ASSERT_TRUE(walletAccount.derivedFrom.has_value());
} }
@ -53,14 +53,14 @@ TEST(Accounts, TestGenerateAccountWithDerivedPath)
constexpr auto testAccountPassword = "password*"; constexpr auto testAccountPassword = "password*";
ScopedTestAccount testAccount(test_info_->name(), testRootAccountName, testAccountPassword, true); ScopedTestAccount testAccount(test_info_->name(), testRootAccountName, testAccountPassword, true);
auto hashedPassword{Utils::hashString(testAccountPassword)}; auto password{Utils::hashPassword(testAccountPassword)};
const auto newTestAccountName = u"test_generated_new_account-name"_qs; const auto newTestAccountName = u"test_generated_new_account-name"_qs;
const auto newTestAccountColor = QColor("fuchsia"); const auto newTestAccountColor = QColor("fuchsia");
const auto newTestAccountEmoji = u""_qs; const auto newTestAccountEmoji = u""_qs;
const auto newTestAccountPath = Status::Constants::General::PathWalletRoot; const auto newTestAccountPath = Status::Constants::General::PathWalletRoot;
const auto chatAccount = testAccount.firstChatAccount(); const auto chatAccount = testAccount.firstChatAccount();
Accounts::generateAccountWithDerivedPath(hashedPassword, newTestAccountName, Accounts::generateAccountWithDerivedPath(password, newTestAccountName,
newTestAccountColor, newTestAccountEmoji, newTestAccountColor, newTestAccountEmoji,
newTestAccountPath, chatAccount.address); newTestAccountPath, chatAccount.address);
const auto updatedAccounts = Accounts::getAccounts(); const auto updatedAccounts = Accounts::getAccounts();
@ -72,13 +72,13 @@ TEST(Accounts, TestGenerateAccountWithDerivedPath)
}); });
ASSERT_NE(newAccountIt, updatedAccounts.end()); ASSERT_NE(newAccountIt, updatedAccounts.end());
const auto &newAccount = *newAccountIt; const auto &newAccount = *newAccountIt;
ASSERT_FALSE(newAccount.address.isEmpty()); ASSERT_FALSE(newAccount.address.get().isEmpty());
ASSERT_FALSE(newAccount.isChat); ASSERT_FALSE(newAccount.isChat);
ASSERT_FALSE(newAccount.isWallet); ASSERT_FALSE(newAccount.isWallet);
ASSERT_EQ(newAccount.color, newTestAccountColor); ASSERT_EQ(newAccount.color, newTestAccountColor);
ASSERT_FALSE(newAccount.derivedFrom.has_value()); ASSERT_FALSE(newAccount.derivedFrom.has_value());
ASSERT_EQ(newAccount.emoji, newTestAccountEmoji); ASSERT_EQ(newAccount.emoji, newTestAccountEmoji);
ASSERT_EQ(newAccount.mixedcaseAddress.toUpper(), newAccount.address.toUpper()); ASSERT_EQ(newAccount.mixedcaseAddress.toUpper(), newAccount.address.get().toUpper());
ASSERT_EQ(newAccount.path, newTestAccountPath); ASSERT_EQ(newAccount.path, newTestAccountPath);
ASSERT_FALSE(newAccount.publicKey.isEmpty()); ASSERT_FALSE(newAccount.publicKey.isEmpty());
} }
@ -91,7 +91,7 @@ TEST(AccountsAPI, TestGenerateAccountWithDerivedPath_WrongPassword)
const auto chatAccount = testAccount.firstChatAccount(); const auto chatAccount = testAccount.firstChatAccount();
try { try {
Accounts::generateAccountWithDerivedPath(Utils::hashString("WrongPassword"), u"test_wrong_pass-name"_qs, Accounts::generateAccountWithDerivedPath(Utils::hashPassword("WrongPassword"), u"test_wrong_pass-name"_qs,
QColor("fuchsia"), "", Status::Constants::General::PathWalletRoot, QColor("fuchsia"), "", Status::Constants::General::PathWalletRoot,
chatAccount.address); chatAccount.address);
FAIL(); FAIL();
@ -111,14 +111,14 @@ TEST(AccountsAPI, TestAddAccountWithMnemonicAndPath)
constexpr auto testAccountPassword = "password*"; constexpr auto testAccountPassword = "password*";
ScopedTestAccount testAccount(test_info_->name(), testRootAccountName, testAccountPassword, true); ScopedTestAccount testAccount(test_info_->name(), testRootAccountName, testAccountPassword, true);
auto hashedPassword{Utils::hashString(testAccountPassword)}; auto password{Utils::hashPassword(testAccountPassword)};
const auto newTestAccountName = u"test_import_from_mnemonic-name"_qs; const auto newTestAccountName = u"test_import_from_mnemonic-name"_qs;
const auto newTestAccountColor = QColor("fuchsia"); const auto newTestAccountColor = QColor("fuchsia");
const auto newTestAccountEmoji = u""_qs; const auto newTestAccountEmoji = u""_qs;
const auto newTestAccountPath = Status::Constants::General::PathWalletRoot; const auto newTestAccountPath = Status::Constants::General::PathWalletRoot;
Accounts::addAccountWithMnemonicAndPath("festival october control quarter husband dish throw couch depth stadium cigar whisper", Accounts::addAccountWithMnemonicAndPath("festival october control quarter husband dish throw couch depth stadium cigar whisper",
hashedPassword, newTestAccountName, newTestAccountColor, newTestAccountEmoji, password, newTestAccountName, newTestAccountColor, newTestAccountEmoji,
newTestAccountPath); newTestAccountPath);
const auto updatedAccounts = Accounts::getAccounts(); const auto updatedAccounts = Accounts::getAccounts();
ASSERT_EQ(updatedAccounts.size(), 3); ASSERT_EQ(updatedAccounts.size(), 3);
@ -129,13 +129,13 @@ TEST(AccountsAPI, TestAddAccountWithMnemonicAndPath)
}); });
ASSERT_NE(newAccountIt, updatedAccounts.end()); ASSERT_NE(newAccountIt, updatedAccounts.end());
const auto &newAccount = *newAccountIt; const auto &newAccount = *newAccountIt;
ASSERT_FALSE(newAccount.address.isEmpty()); ASSERT_FALSE(newAccount.address.get().isEmpty());
ASSERT_FALSE(newAccount.isChat); ASSERT_FALSE(newAccount.isChat);
ASSERT_FALSE(newAccount.isWallet); ASSERT_FALSE(newAccount.isWallet);
ASSERT_EQ(newAccount.color, newTestAccountColor); ASSERT_EQ(newAccount.color, newTestAccountColor);
ASSERT_FALSE(newAccount.derivedFrom.has_value()); ASSERT_FALSE(newAccount.derivedFrom.has_value());
ASSERT_EQ(newAccount.emoji, newTestAccountEmoji); ASSERT_EQ(newAccount.emoji, newTestAccountEmoji);
ASSERT_EQ(newAccount.mixedcaseAddress.toUpper(), newAccount.address.toUpper()); ASSERT_EQ(newAccount.mixedcaseAddress.toUpper(), newAccount.address.get().toUpper());
ASSERT_EQ(newAccount.path, newTestAccountPath); ASSERT_EQ(newAccount.path, newTestAccountPath);
ASSERT_FALSE(newAccount.publicKey.isEmpty()); ASSERT_FALSE(newAccount.publicKey.isEmpty());
} }
@ -147,7 +147,7 @@ TEST(AccountsAPI, TestAddAccountWithMnemonicAndPath_WrongMnemonicWorks)
constexpr auto testAccountPassword = "password*"; constexpr auto testAccountPassword = "password*";
ScopedTestAccount testAccount(test_info_->name(), testRootAccountName, testAccountPassword, true); ScopedTestAccount testAccount(test_info_->name(), testRootAccountName, testAccountPassword, true);
auto hashedPassword{Utils::hashString(testAccountPassword)}; auto password{Utils::hashPassword(testAccountPassword)};
const auto newTestAccountName = u"test_import_from_wrong_mnemonic-name"_qs; const auto newTestAccountName = u"test_import_from_wrong_mnemonic-name"_qs;
const auto newTestAccountColor = QColor("fuchsia"); const auto newTestAccountColor = QColor("fuchsia");
const auto newTestAccountEmoji = u""_qs; const auto newTestAccountEmoji = u""_qs;
@ -155,7 +155,7 @@ TEST(AccountsAPI, TestAddAccountWithMnemonicAndPath_WrongMnemonicWorks)
// Added an inexistent word. The mnemonic is not checked. // Added an inexistent word. The mnemonic is not checked.
Accounts::addAccountWithMnemonicAndPath("october control quarter husband dish throw couch depth stadium cigar waku", Accounts::addAccountWithMnemonicAndPath("october control quarter husband dish throw couch depth stadium cigar waku",
hashedPassword, newTestAccountName, newTestAccountColor, newTestAccountEmoji, password, newTestAccountName, newTestAccountColor, newTestAccountEmoji,
newTestAccountPath); newTestAccountPath);
const auto updatedAccounts = Accounts::getAccounts(); const auto updatedAccounts = Accounts::getAccounts();
@ -168,13 +168,13 @@ TEST(AccountsAPI, TestAddAccountWithMnemonicAndPath_WrongMnemonicWorks)
ASSERT_NE(newAccountIt, updatedAccounts.end()); ASSERT_NE(newAccountIt, updatedAccounts.end());
const auto &newAccount = *newAccountIt; const auto &newAccount = *newAccountIt;
ASSERT_FALSE(newAccount.address.isEmpty()); ASSERT_FALSE(newAccount.address.get().isEmpty());
ASSERT_FALSE(newAccount.isChat); ASSERT_FALSE(newAccount.isChat);
ASSERT_FALSE(newAccount.isWallet); ASSERT_FALSE(newAccount.isWallet);
ASSERT_EQ(newAccount.color, newTestAccountColor); ASSERT_EQ(newAccount.color, newTestAccountColor);
ASSERT_FALSE(newAccount.derivedFrom.has_value()); ASSERT_FALSE(newAccount.derivedFrom.has_value());
ASSERT_EQ(newAccount.emoji, newTestAccountEmoji); ASSERT_EQ(newAccount.emoji, newTestAccountEmoji);
ASSERT_EQ(newAccount.mixedcaseAddress.toUpper(), newAccount.address.toUpper()); ASSERT_EQ(newAccount.mixedcaseAddress.toUpper(), newAccount.address.get().toUpper());
ASSERT_EQ(newAccount.path, newTestAccountPath); ASSERT_EQ(newAccount.path, newTestAccountPath);
ASSERT_FALSE(newAccount.publicKey.isEmpty()); ASSERT_FALSE(newAccount.publicKey.isEmpty());
} }
@ -189,7 +189,7 @@ TEST(AccountsAPI, TestAddAccountWatch)
const auto newTestAccountColor = QColor("fuchsia"); const auto newTestAccountColor = QColor("fuchsia");
const auto newTestAccountEmoji = u""_qs; const auto newTestAccountEmoji = u""_qs;
Accounts::addAccountWatch("0x145b6B821523afFC346774b41ACC7b77A171BbA4", newTestAccountName, newTestAccountColor, newTestAccountEmoji); Accounts::addAccountWatch(Accounts::EOAddress("0x145b6B821523afFC346774b41ACC7b77A171BbA4"), newTestAccountName, newTestAccountColor, newTestAccountEmoji);
const auto updatedAccounts = Accounts::getAccounts(); const auto updatedAccounts = Accounts::getAccounts();
ASSERT_EQ(updatedAccounts.size(), 3); ASSERT_EQ(updatedAccounts.size(), 3);
@ -199,14 +199,14 @@ TEST(AccountsAPI, TestAddAccountWatch)
}); });
ASSERT_NE(newAccountIt, updatedAccounts.end()); ASSERT_NE(newAccountIt, updatedAccounts.end());
const auto &newAccount = *newAccountIt; const auto &newAccount = *newAccountIt;
ASSERT_FALSE(newAccount.address.isEmpty()); ASSERT_FALSE(newAccount.address.get().isEmpty());
ASSERT_FALSE(newAccount.isChat); ASSERT_FALSE(newAccount.isChat);
ASSERT_FALSE(newAccount.isWallet); ASSERT_FALSE(newAccount.isWallet);
ASSERT_EQ(newAccount.color, newTestAccountColor); ASSERT_EQ(newAccount.color, newTestAccountColor);
ASSERT_FALSE(newAccount.derivedFrom.has_value()); ASSERT_FALSE(newAccount.derivedFrom.has_value());
ASSERT_EQ(newAccount.emoji, newTestAccountEmoji); ASSERT_EQ(newAccount.emoji, newTestAccountEmoji);
ASSERT_EQ(newAccount.mixedcaseAddress.toUpper(), newAccount.address.toUpper()); ASSERT_EQ(newAccount.mixedcaseAddress.toUpper(), newAccount.address.get().toUpper());
ASSERT_TRUE(newAccount.path.isEmpty()); ASSERT_TRUE(newAccount.path.get().isEmpty());
ASSERT_TRUE(newAccount.publicKey.isEmpty()); ASSERT_TRUE(newAccount.publicKey.isEmpty());
} }
@ -220,7 +220,7 @@ TEST(AccountsAPI, TestDeleteAccount)
const auto newTestAccountColor = QColor("fuchsia"); const auto newTestAccountColor = QColor("fuchsia");
const auto newTestAccountEmoji = u""_qs; const auto newTestAccountEmoji = u""_qs;
Accounts::addAccountWatch("0x145b6B821523afFC346774b41ACC7b77A171BbA4", newTestAccountName, newTestAccountColor, newTestAccountEmoji); Accounts::addAccountWatch(Accounts::EOAddress("0x145b6B821523afFC346774b41ACC7b77A171BbA4"), newTestAccountName, newTestAccountColor, newTestAccountEmoji);
const auto updatedAccounts = Accounts::getAccounts(); const auto updatedAccounts = Accounts::getAccounts();
ASSERT_EQ(updatedAccounts.size(), 3); ASSERT_EQ(updatedAccounts.size(), 3);

View File

@ -32,11 +32,11 @@ TEST(WalletApi, TestGetDerivedAddressesForPath)
const auto rootAccount = testAccount.onboardingController()->accountsService()->getLoggedInAccount(); const auto rootAccount = testAccount.onboardingController()->accountsService()->getLoggedInAccount();
ASSERT_EQ(rootAccount.address, walletAccount.derivedFrom.value()); ASSERT_EQ(rootAccount.address, walletAccount.derivedFrom.value());
const auto hashedPassword{Utils::hashString(testAccountPassword)}; const auto password{Utils::hashPassword(testAccountPassword)};
const auto testPath = Status::Constants::General::PathWalletRoot; const auto testPath = Status::Constants::General::PathWalletRoot;
// chatAccount.address // chatAccount.address
const auto chatDerivedAddresses = Wallet::getDerivedAddressesForPath(hashedPassword, chatAccount.address, testPath, 3, 1); const auto chatDerivedAddresses = Wallet::getDerivedAddressesForPath(password, chatAccount.address, testPath, 3, 1);
// Check that no change is done // Check that no change is done
const auto updatedAccounts = Accounts::getAccounts(); const auto updatedAccounts = Accounts::getAccounts();
ASSERT_EQ(updatedAccounts.size(), 2); ASSERT_EQ(updatedAccounts.size(), 2);
@ -47,14 +47,14 @@ TEST(WalletApi, TestGetDerivedAddressesForPath)
// all hasActivity are false // all hasActivity are false
ASSERT_TRUE(std::none_of(chatDerivedAddresses.begin(), chatDerivedAddresses.end(), [](const auto& a) { return a.hasActivity; })); ASSERT_TRUE(std::none_of(chatDerivedAddresses.begin(), chatDerivedAddresses.end(), [](const auto& a) { return a.hasActivity; }));
// all address are valid // all address are valid
ASSERT_TRUE(std::none_of(chatDerivedAddresses.begin(), chatDerivedAddresses.end(), [](const auto& a) { return a.address.isEmpty(); })); ASSERT_TRUE(std::none_of(chatDerivedAddresses.begin(), chatDerivedAddresses.end(), [](const auto& a) { return a.address.get().isEmpty(); }));
const auto walletDerivedAddresses = Wallet::getDerivedAddressesForPath(hashedPassword, walletAccount.address, testPath, 2, 1); const auto walletDerivedAddresses = Wallet::getDerivedAddressesForPath(password, walletAccount.address, testPath, 2, 1);
ASSERT_EQ(walletDerivedAddresses.size(), 2); ASSERT_EQ(walletDerivedAddresses.size(), 2);
// all alreadyCreated are false // all alreadyCreated are false
ASSERT_TRUE(std::none_of(walletDerivedAddresses.begin(), walletDerivedAddresses.end(), [](const auto& a) { return a.alreadyCreated; })); ASSERT_TRUE(std::none_of(walletDerivedAddresses.begin(), walletDerivedAddresses.end(), [](const auto& a) { return a.alreadyCreated; }));
const auto rootDerivedAddresses = Wallet::getDerivedAddressesForPath(hashedPassword, rootAccount.address, testPath, 4, 1); const auto rootDerivedAddresses = Wallet::getDerivedAddressesForPath(password, rootAccount.address, testPath, 4, 1);
ASSERT_EQ(rootDerivedAddresses.size(), 4); ASSERT_EQ(rootDerivedAddresses.size(), 4);
ASSERT_EQ(std::count_if(rootDerivedAddresses.begin(), rootDerivedAddresses.end(), [](const auto& a) { return a.alreadyCreated; }), 1); ASSERT_EQ(std::count_if(rootDerivedAddresses.begin(), rootDerivedAddresses.end(), [](const auto& a) { return a.alreadyCreated; }), 1);
const auto &existingAddress = *std::find_if(rootDerivedAddresses.begin(), rootDerivedAddresses.end(), [](const auto& a) { return a.alreadyCreated; }); const auto &existingAddress = *std::find_if(rootDerivedAddresses.begin(), rootDerivedAddresses.end(), [](const auto& a) { return a.alreadyCreated; });