/* * This file is part of the Flowee project * Copyright (C) 2020-2022 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "AccountInfo.h" #include "WalletHistoryModel.h" #include "FloweePay.h" #include #include #include #include #include AccountInfo::AccountInfo(Wallet *wallet, QObject *parent) : QObject(parent), m_wallet(wallet) { connect(wallet, SIGNAL(utxosChanged()), this, SIGNAL(utxosChanged()), Qt::QueuedConnection); connect(wallet, SIGNAL(balanceChanged()), this, SLOT(balanceHasChanged()), Qt::QueuedConnection); connect(wallet, SIGNAL(lastBlockSynchedChanged()), this, SIGNAL(lastBlockSynchedChanged()), Qt::QueuedConnection); connect(wallet, SIGNAL(lastBlockSynchedChanged()), this, SIGNAL(timeBehindChanged()), Qt::QueuedConnection); connect(wallet, SIGNAL(paymentRequestsChanged()), this, SIGNAL(paymentRequestsChanged()), Qt::QueuedConnection); connect(wallet, SIGNAL(encryptionChanged()), this, SLOT(walletEncryptionChanged()), Qt::QueuedConnection); connect(FloweePay::instance(), SIGNAL(headerChainHeightChanged()), this, SIGNAL(timeBehindChanged())); } int AccountInfo::id() const { return m_wallet->segment()->segmentId(); } double AccountInfo::balanceConfirmed() const { return static_cast(m_wallet->balanceConfirmed()); } double AccountInfo::balanceUnconfirmed() const { return static_cast(m_wallet->balanceUnconfirmed()); } double AccountInfo::balanceImmature() const { return static_cast(m_wallet->balanceImmature()); } int AccountInfo::unspentOutputCount() const { return m_wallet->unspentOutputCount(); } int AccountInfo::historicalOutputCount() const { return m_wallet->historicalOutputCount(); } void AccountInfo::setName(const QString &name) { if (m_wallet->name() == name) return; m_wallet->setName(name); emit nameChanged(); } QString AccountInfo::name() const { return m_wallet->name(); } int AccountInfo::lastBlockSynched() const { if (!m_wallet->segment()) return 0; return m_wallet->segment()->lastBlockSynched(); } QDateTime AccountInfo::lastBlockSynchedTime() const { if (!m_wallet->segment() || m_wallet->segment()->lastBlockSynched() < 1) return QDateTime(); auto timestamp = FloweePay::instance()->p2pNet()->blockchain().block(m_wallet->segment()->lastBlockSynched()).nTime; if (timestamp == 0) return QDateTime(); return QDateTime::fromTime_t(timestamp); } QString AccountInfo::timeBehind() const { const int accountHeight = lastBlockSynched(); if (accountHeight <= 0) // For accounts that only expect tx in the future. return tr("Up to date"); const int chainHeight = FloweePay::instance()->chainHeight(); const int diff = chainHeight - accountHeight; if (diff < 0) // we are ahead??? return "--"; auto days = diff / 144.; auto weeks = diff / 1008.; if (days > 10) return tr("%1 weeks behind", "", std::ceil(weeks)).arg(std::ceil(weeks)); auto hours = diff / 6.; if (hours > 48) return tr("%1 days behind", "", std::ceil(days)).arg(std::ceil(days)); if (diff == 0) return tr("Up to date"); if (diff < 3 && !isArchived()) return tr("Updating"); return tr("%1 hours behind", "", std::ceil(hours)).arg(std::ceil(hours)); } WalletHistoryModel *AccountInfo::historyModel() { if (m_model == nullptr) { m_model.reset(new WalletHistoryModel(m_wallet, this)); } return m_model.get(); } WalletSecretsModel *AccountInfo::secretsModel() { if (m_secretsModel == nullptr) { m_secretsModel.reset(new WalletSecretsModel(m_wallet, this)); } return m_secretsModel.get(); } QDateTime AccountInfo::lastMinedTransaction() const { assert(m_wallet); const int blockHeight = m_wallet->lastTransactionTimestamp(); if (blockHeight <= 0) return QDateTime(); auto timestamp = FloweePay::instance()->p2pNet()->blockchain().block(blockHeight).nTime; if (timestamp == 0) return QDateTime(); return QDateTime::fromTime_t(timestamp); } bool AccountInfo::isArchived() const { auto segment =m_wallet->segment(); if (!segment) return true; return segment->priority() == PrivacySegment::OnlyManual; } void AccountInfo::setIsArchived(bool archived) { auto segment = m_wallet->segment(); if (!segment) return; if ((segment->priority() == PrivacySegment::OnlyManual) == archived) return; segment->setPriority(archived ? PrivacySegment::OnlyManual : PrivacySegment::Normal); emit isArchivedChanged(); if (!archived && !FloweePay::instance()->isOffline()) { // make sure that we get peers for the no longer archived wallet. FloweePay::instance()->p2pNet()->addAction(); } if (archived) closeWallet(); } void AccountInfo::balanceHasChanged() { emit balanceChanged(); /* us getting called is very likely due to a new transaction that has been made known to the wallet. */ if (!m_hasFreshTransactions && m_wallet->isDecrypted()) { // check this const int blockHeight = m_wallet->lastTransactionTimestamp(); if (m_lastTxHeight < blockHeight) { setHasFreshTransactions(true); m_lastTxHeight = blockHeight; } if (blockHeight == 0) // an unconfirmed one setHasFreshTransactions(true); } } void AccountInfo::walletEncryptionChanged() { auto m = m_model.release(); if (m) m->deleteLater(); auto s = m_secretsModel.release(); if (s) s->deleteLater(); emit modelsChanged(); emit encryptionChanged(); emit nameChanged(); } bool AccountInfo::hasFreshTransactions() const { return m_hasFreshTransactions; } void AccountInfo::setHasFreshTransactions(bool fresh) { if (m_hasFreshTransactions == fresh) return; m_hasFreshTransactions = fresh; emit hasFreshTransactionsChanged(); } bool AccountInfo::needsPinToPay() const { return m_wallet->encryption() == Wallet::SecretsEncrypted; } bool AccountInfo::needsPinToOpen() const { return m_wallet->encryption() == Wallet::FullyEncrypted; } bool AccountInfo::isDecrypted() const { if (m_wallet->encryption() == Wallet::NotEncrypted) return true; const auto &secrets = m_wallet->walletSecrets(); if (secrets.empty()) return false; for (auto i = secrets.begin(); i != secrets.end(); ++i) { if (i->second.privKey.isValid()) return true; } return false; } void AccountInfo::setDefaultWallet(bool isDefault) { auto segment = m_wallet->segment(); if (!segment) return; if ((segment->priority() == PrivacySegment::First) == isDefault) return; segment->setPriority(isDefault ? PrivacySegment::First : PrivacySegment::Normal); emit isDefaultWalletChanged(); } bool AccountInfo::isDefaultWallet() { auto segment =m_wallet->segment(); if (!segment) return false; return segment->priority() == PrivacySegment::First; } bool AccountInfo::userOwnedWallet() { return m_wallet->userOwnedWallet(); } QList AccountInfo::paymentRequests() const { QList answer; for (auto *pr : m_wallet->paymentRequests()) { answer.append(pr); } return answer; } TransactionInfo* AccountInfo::txInfo(int walletIndex, QObject *parent) { Q_ASSERT(parent); auto info = new TransactionInfo(parent); m_wallet->fetchTransactionInfo(info, walletIndex); return info; } QObject *AccountInfo::createPaymentRequest(QObject *parent) { return new PaymentRequest(m_wallet, parent); } void AccountInfo::encryptPinToPay(const QString &password) { m_wallet->setEncryption(Wallet::SecretsEncrypted, password); } void AccountInfo::encryptPinToOpen(const QString &password) { if (m_closeWalletTimer) m_closeWalletTimer->stop(); m_wallet->setEncryption(Wallet::FullyEncrypted, password); } bool AccountInfo::decrypt(const QString &password) { const bool shouldAutoclose = m_wallet->encryption() == Wallet::SecretsEncrypted; const bool success = m_wallet->decrypt(password); if (shouldAutoclose && success) { if (!m_closeWalletTimer) { m_closeWalletTimer = new QTimer(this); connect (m_closeWalletTimer, &QTimer::timeout, m_wallet, [=]() { m_wallet->forgetEncryptedSecrets(); }); } m_closeWalletTimer->stop(); m_closeWalletTimer->start(200 * 1000); // 200s } return success; } void AccountInfo::closeWallet() { // this forgets secrets m_wallet->forgetEncryptedSecrets(); if (m_closeWalletTimer) m_closeWalletTimer->stop(); setHasFreshTransactions(false); } bool AccountInfo::isSingleAddressAccount() const { return m_wallet->isSingleAddressWallet(); } bool AccountInfo::isHDWallet() const { return m_wallet->isHDWallet(); } QString AccountInfo::hdWalletMnemonic() const { return m_wallet->hdWalletMnemonic(); } QString AccountInfo::hdDerivationPath() const { return m_wallet->derivationPath(); } QString AccountInfo::xpub() const { return m_wallet->xpub(); }