2022-01-06 19:29:19 +00:00
# include "accounts/service.h"
2022-02-22 08:02:34 +00:00
2022-01-06 19:29:19 +00:00
# include "accounts/account.h"
# include "accounts/generated_account.h"
# include "accounts/service_interface.h"
# include "app_service.h"
# include "backend/accounts.h"
# include "backend/utils.h"
# include "constants.h"
# include "signing-phrases.h"
2022-02-22 08:02:34 +00:00
2022-01-06 19:29:19 +00:00
# include <QDebug>
# include <QFile>
# include <QJsonArray>
# include <QJsonObject>
# include <QRandomGenerator>
2022-02-22 08:02:34 +00:00
# include <QStringList>
2022-01-06 19:29:19 +00:00
# include <QUuid>
2022-02-22 08:02:34 +00:00
namespace
{
const QVector < QString > Paths { Backend : : Accounts : : PathWalletRoot ,
Backend : : Accounts : : PathEIP1581 ,
Backend : : Accounts : : PathWhisper ,
Backend : : Accounts : : PathDefaultWallet } ;
QString generateSigningPhrase ( int count )
{
QStringList words ;
for ( int i = 0 ; i < count ; i + + )
{
words . append ( phrases [ QRandomGenerator : : global ( ) - > bounded ( static_cast < int > ( phrases . size ( ) ) ) ] ) ;
}
return words . join ( " " ) ;
}
QJsonObject prepareAccountJsonObject ( const Accounts : : GeneratedAccountDto & account )
{
return { { " name " , account . alias } ,
{ " address " , account . address } ,
{ " photo-path " , account . identicon } ,
{ " identicon " , account . identicon } ,
{ " key-uid " , account . keyUid } ,
{ " keycard-pairing " , QJsonValue ( ) } } ;
}
QJsonArray prepareSubaccountJsonObject ( Accounts : : GeneratedAccountDto account )
{
return { QJsonObject { { " public-key " , account . derivedAccounts . defaultWallet . publicKey } ,
{ " address " , account . derivedAccounts . defaultWallet . address } ,
{ " color " , " #4360df " } ,
{ " wallet " , true } ,
{ " path " , Backend : : Accounts : : PathDefaultWallet } ,
{ " name " , " Status account " } } ,
QJsonObject { { " public-key " , account . derivedAccounts . whisper . publicKey } ,
{ " address " , account . derivedAccounts . whisper . address } ,
{ " path " , Backend : : Accounts : : PathWhisper } ,
{ " name " , account . alias } ,
{ " identicon " , account . identicon } ,
{ " chat " , true } } } ;
}
QJsonObject prepareAccountSettingsJsonObject ( const Accounts : : GeneratedAccountDto & account , QString installationId )
{
auto defaultNetworks = QFile { " :/resources/default-networks.json " } ;
defaultNetworks . open ( QIODevice : : ReadOnly ) ;
QString defaultNetworksContent = defaultNetworks . readAll ( ) . replace ( " %INFURA_KEY% " , INFURA_KEY ) ;
QJsonArray defaultNetworksJson = QJsonDocument : : fromJson ( defaultNetworksContent . toUtf8 ( ) ) . array ( ) ;
return { { " key-uid " , account . keyUid } ,
{ " mnemonic " , account . mnemonic } ,
{ " public-key " , account . derivedAccounts . whisper . publicKey } ,
{ " name " , account . alias } ,
{ " 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 } ,
{ " networks/networks " , defaultNetworksJson } ,
{ " currency " , " usd " } ,
{ " identicon " , account . identicon } ,
{ " waku-enabled " , true } ,
{ " wallet/visible-tokens " , { { Constants : : DefaultNetworkName , QJsonArray { " SNT " } } } } ,
{ " appearance " , 0 } ,
{ " networks/current-network " , Constants : : DefaultNetworkName } ,
{ " installation-id " , installationId } } ;
}
QJsonArray getNodes ( const QJsonObject & fleet , const QString & nodeType )
{
auto nodes = fleet [ nodeType ] . toObject ( ) ;
QJsonArray result ;
for ( const auto & node : nodes )
{
result < < node ;
}
return result ;
}
QJsonObject getDefaultNodeConfig ( const QString & installationId )
2022-01-06 19:29:19 +00:00
{
2022-02-22 08:02:34 +00:00
auto nodeConfig = QFile { " :/resources/node-config.json " } ;
nodeConfig . open ( QIODevice : : ReadOnly ) ;
QString nodeConfigContent = nodeConfig . readAll ( ) ;
2022-01-06 19:29:19 +00:00
2022-02-22 08:02:34 +00:00
nodeConfigContent = nodeConfigContent . replace ( " %INSTALLATIONID% " , installationId ) ;
nodeConfigContent = nodeConfigContent . replace ( " %INFURA_KEY% " , INFURA_KEY ) ;
QJsonObject nodeConfigJson = QJsonDocument : : fromJson ( nodeConfigContent . toUtf8 ( ) ) . object ( ) ;
auto fleets = QFile { " :/resources/fleets.json " } ;
fleets . open ( QIODevice : : ReadOnly ) ;
QJsonObject fleetsJson = QJsonDocument : : fromJson ( fleets . readAll ( ) ) . object ( ) [ " fleets " ] . toObject ( ) ;
auto fleet = fleetsJson [ Constants : : Fleet : : Prod ] . toObject ( ) ;
2022-01-06 19:29:19 +00:00
2022-02-22 08:02:34 +00:00
QJsonObject clusterConfig = nodeConfigJson [ " ClusterConfig " ] . toObject ( ) ;
clusterConfig [ " Fleet " ] = Constants : : Fleet : : Prod ;
clusterConfig [ " BootNodes " ] = getNodes ( fleet , Constants : : FleetNodes : : Bootnodes ) ;
clusterConfig [ " TrustedMailServers " ] = getNodes ( fleet , Constants : : FleetNodes : : Mailservers ) ;
clusterConfig [ " StaticNodes " ] = getNodes ( fleet , Constants : : FleetNodes : : Whisper ) ;
clusterConfig [ " RendezvousNodes " ] = getNodes ( fleet , Constants : : FleetNodes : : Rendezvous ) ;
clusterConfig [ " RelayNodes " ] = getNodes ( fleet , Constants : : FleetNodes : : Waku ) ;
clusterConfig [ " StoreNodes " ] = getNodes ( fleet , Constants : : FleetNodes : : Waku ) ;
clusterConfig [ " FilterNodes " ] = getNodes ( fleet , Constants : : FleetNodes : : Waku ) ;
clusterConfig [ " LightpushNodes " ] = getNodes ( fleet , Constants : : FleetNodes : : Waku ) ;
nodeConfigJson [ " ClusterConfig " ] = clusterConfig ;
return nodeConfigJson ;
}
} // namespace
namespace Accounts
{
2022-01-06 19:29:19 +00:00
void Service : : init ( )
{
2022-02-22 08:02:34 +00:00
auto response = Backend : : Accounts : : generateAddresses ( Paths ) ;
2022-02-21 18:03:38 +00:00
foreach ( QJsonValue generatedAddressJson , response . m_result )
{
auto gAcc = toGeneratedAccountDto ( generatedAddressJson ) ;
gAcc . alias = generateAlias ( gAcc . derivedAccounts . whisper . publicKey ) ;
gAcc . identicon = generateIdenticon ( gAcc . derivedAccounts . whisper . publicKey ) ;
m_generatedAccounts < < gAcc ;
}
2022-01-06 19:29:19 +00:00
}
QVector < AccountDto > Service : : openedAccounts ( )
{
2022-02-21 18:03:38 +00:00
// TODO: if there's an exception, should we return an empty result? or should we look into using
// std::expected or std::optional or boost outcome https://www.boost.org/doc/libs/1_75_0/libs/outcome/doc/html/index.html
try
{
auto response = Backend : : Accounts : : openAccounts ( Constants : : applicationPath ( Constants : : DataDir ) ) ;
QJsonArray multiAccounts = response . m_result ;
QVector < AccountDto > result ;
foreach ( const QJsonValue & value , multiAccounts )
{
result < < toAccountDto ( value ) ;
}
return result ;
}
catch ( Backend : : RpcException & e )
{
qWarning ( ) < < " error: methodName=openedAccounts, errDescription= " < < e . what ( ) ;
2022-02-22 08:02:34 +00:00
return { } ;
2022-02-21 18:03:38 +00:00
}
2022-01-06 19:29:19 +00:00
}
QVector < GeneratedAccountDto > Service : : generatedAccounts ( )
{
2022-02-21 18:03:38 +00:00
if ( m_generatedAccounts . length ( ) = = 0 )
{
2022-02-22 08:02:34 +00:00
qWarning ( ) < < " There was some issue initiating account service " ;
return { } ;
2022-02-21 18:03:38 +00:00
}
2022-01-06 19:29:19 +00:00
2022-02-21 18:03:38 +00:00
return m_generatedAccounts ;
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
bool Service : : setupAccount ( const QString & accountId , const QString & password )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
// TODO: would it make sense to use std::expected or std::optional or boost outcome https://www.boost.org/doc/libs/1_75_0/libs/outcome/doc/html/index.html
try
{
2022-02-22 08:02:34 +00:00
const QString installationId ( QUuid : : createUuid ( ) . toString ( QUuid : : WithoutBraces ) ) ;
const QJsonObject accountData ( Service : : getAccountDataForAccountId ( accountId ) ) ;
const QJsonArray subAccountData ( Service : : getSubaccountDataForAccountId ( accountId ) ) ;
const QJsonObject settings ( Service : : getAccountSettings ( accountId , installationId ) ) ;
const QJsonObject nodeConfig ( getDefaultNodeConfig ( installationId ) ) ;
2022-02-21 18:03:38 +00:00
2022-02-22 08:02:34 +00:00
const QString hashedPassword ( Backend : : Utils : : hashString ( password ) ) ;
2022-02-21 18:03:38 +00:00
2022-02-22 08:02:34 +00:00
Service : : storeDerivedAccounts ( accountId , hashedPassword , Paths ) ;
2022-02-21 18:03:38 +00:00
m_loggedInAccount =
Service : : saveAccountAndLogin ( hashedPassword , accountData , subAccountData , settings , nodeConfig ) ;
return Service : : getLoggedInAccount ( ) . isValid ( ) ;
}
2022-02-22 08:02:34 +00:00
catch ( std : : exception & e )
2022-02-21 18:03:38 +00:00
{
qWarning ( ) < < " error: methodName=setupAccount, errDescription= " < < e . what ( ) ;
return false ;
}
2022-01-06 19:29:19 +00:00
}
AccountDto Service : : getLoggedInAccount ( )
{
2022-02-21 18:03:38 +00:00
return m_loggedInAccount ;
2022-01-06 19:29:19 +00:00
}
GeneratedAccountDto Service : : getImportedAccount ( )
{
2022-02-21 18:03:38 +00:00
return m_importedAccount ;
2022-01-06 19:29:19 +00:00
}
bool Service : : isFirstTimeAccountLogin ( )
{
2022-02-21 18:03:38 +00:00
return m_isFirstTimeAccountLogin ;
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
QString Service : : validateMnemonic ( const QString & mnemonic )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
// TODO:
return " " ;
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
bool Service : : importMnemonic ( const QString & mnemonic )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
// TODO:
return false ;
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
QString Service : : login ( const AccountDto & account , const QString & password )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
// TODO: would it make sense to use std::expected or std::optional or boost outcome https://www.boost.org/doc/libs/1_75_0/libs/outcome/doc/html/index.html
try
{
QString hashedPassword ( Backend : : Utils : : hashString ( password ) ) ;
QString thumbnailImage ;
QString largeImage ;
foreach ( const Accounts : : Image & img , account . images )
{
if ( img . imgType = = " thumbnail " )
{
thumbnailImage = img . uri ;
}
else if ( img . imgType = = " large " )
{
largeImage = img . uri ;
}
}
auto response = Backend : : Accounts : : login (
account . name , account . keyUid , hashedPassword , account . identicon , thumbnailImage , largeImage ) ;
// TODO: check response for errors
qDebug ( ) < < " Account logged in " ;
m_loggedInAccount = account ;
return " " ;
}
2022-02-22 08:02:34 +00:00
catch ( std : : exception & e )
2022-02-21 18:03:38 +00:00
{
qWarning ( ) < < " error: methodName=login, errDescription= " < < e . what ( ) ;
return e . what ( ) ;
}
2022-01-06 19:29:19 +00:00
}
void Service : : clear ( )
{
2022-02-21 18:03:38 +00:00
m_generatedAccounts . clear ( ) ;
m_loggedInAccount = Accounts : : AccountDto ( ) ;
m_importedAccount = Accounts : : GeneratedAccountDto ( ) ;
m_isFirstTimeAccountLogin = false ;
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
QString Service : : generateAlias ( const QString & publicKey )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
return Backend : : Accounts : : generateAlias ( publicKey ) . m_result ;
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
QString Service : : generateIdenticon ( const QString & publicKey )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
return Backend : : Accounts : : generateIdenticon ( publicKey ) . m_result ;
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
bool Service : : verifyAccountPassword ( const QString & account , const QString & password )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
// TODO:
return false ;
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
DerivedAccounts
Service : : storeDerivedAccounts ( const QString & accountId , const QString & hashedPassword , const QVector < QString > & paths )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
try
{
auto response = Backend : : Accounts : : storeDerivedAccounts ( accountId , hashedPassword , paths ) ;
return toDerivedAccounts ( response . m_result ) ;
}
catch ( Backend : : RpcException & e )
{
qWarning ( ) < < e . what ( ) ;
2022-02-22 08:02:34 +00:00
return { } ; // TODO: should it return empty?
2022-02-21 18:03:38 +00:00
}
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
Accounts : : AccountDto Service : : saveAccountAndLogin ( const QString & hashedPassword ,
const QJsonObject & account ,
const QJsonArray & subaccounts ,
const QJsonObject & settings ,
const QJsonObject & config )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
// TODO: would it make sense to use std::expected or std::optional or boost outcome https://www.boost.org/doc/libs/1_75_0/libs/outcome/doc/html/index.html
try
{
auto response = Backend : : Accounts : : saveAccountAndLogin ( hashedPassword , account , subaccounts , settings , config ) ;
m_isFirstTimeAccountLogin = true ;
return toAccountDto ( account ) ;
}
2022-02-22 08:02:34 +00:00
catch ( std : : exception & e )
2022-02-21 18:03:38 +00:00
{
qWarning ( ) < < " error: methodName=saveAccountAndLogin, errDescription= " < < e . what ( ) ;
2022-02-22 08:02:34 +00:00
return { } ;
2022-02-21 18:03:38 +00:00
}
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
QJsonObject Service : : getAccountDataForAccountId ( const QString & accountId )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
foreach ( const GeneratedAccountDto & acc , m_generatedAccounts )
{
if ( acc . id = = accountId )
{
2022-02-22 08:02:34 +00:00
return prepareAccountJsonObject ( acc ) ;
2022-02-21 18:03:38 +00:00
}
}
if ( m_importedAccount . isValid ( ) )
{
if ( m_importedAccount . id = = accountId )
{
2022-02-22 08:02:34 +00:00
return prepareAccountJsonObject ( m_importedAccount ) ;
2022-02-21 18:03:38 +00:00
}
}
// TODO: Should we use instead a std::optional?
throw std : : runtime_error ( " account not found " ) ;
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
QJsonArray Service : : getSubaccountDataForAccountId ( const QString & accountId )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
foreach ( const GeneratedAccountDto & acc , m_generatedAccounts )
{
if ( acc . id = = accountId )
{
return prepareSubaccountJsonObject ( acc ) ;
}
}
if ( m_importedAccount . isValid ( ) )
{
if ( m_importedAccount . id = = accountId )
{
return prepareSubaccountJsonObject ( m_importedAccount ) ;
}
}
// TODO: Should we use instead a std::optional?
throw std : : runtime_error ( " account not found " ) ;
2022-01-06 19:29:19 +00:00
}
2022-02-22 08:02:34 +00:00
QJsonObject Service : : getAccountSettings ( const QString & accountId , const QString & installationId )
2022-01-06 19:29:19 +00:00
{
2022-02-21 18:03:38 +00:00
foreach ( const GeneratedAccountDto & acc , m_generatedAccounts )
if ( acc . id = = accountId )
{
2022-02-22 08:02:34 +00:00
return prepareAccountSettingsJsonObject ( acc , installationId ) ;
2022-02-21 18:03:38 +00:00
}
if ( m_importedAccount . isValid ( ) )
{
if ( m_importedAccount . id = = accountId )
{
2022-02-22 08:02:34 +00:00
return prepareAccountSettingsJsonObject ( m_importedAccount , installationId ) ;
2022-02-21 18:03:38 +00:00
}
}
// TODO: Should we use instead a std::optional?
throw std : : runtime_error ( " account not found " ) ;
2022-01-06 19:29:19 +00:00
}
} // namespace Accounts