diff --git a/src/gridcoin/researcher.cpp b/src/gridcoin/researcher.cpp index de239de2a3..1862e35bb4 100644 --- a/src/gridcoin/researcher.cpp +++ b/src/gridcoin/researcher.cpp @@ -401,9 +401,10 @@ std::optional FallbackToCpidByEmail( //! \param projects Map of local projects loaded from BOINC's client_state.xml //! file. //! -void DetectSplitCpid(const MiningProjectMap& projects) +bool DetectSplitCpid(const MiningProjectMap& projects) { std::unordered_map eligible_cpids; + bool mismatched_email = false; for (const auto& project_pair : projects) { if (project_pair.second.Eligible()) { @@ -411,9 +412,13 @@ void DetectSplitCpid(const MiningProjectMap& projects) project_pair.second.m_cpid, project_pair.second.m_name); } + + if (project_pair.second.m_error == MiningProject::Error::MISMATCHED_CPID) { + mismatched_email = true; + } } - if (eligible_cpids.size() > 1) { + if (mismatched_email || eligible_cpids.size() > 1) { std::string warning = "WARNING: Detected potential CPID split. "; warning += "Eligible CPIDs: \n"; @@ -423,7 +428,11 @@ void DetectSplitCpid(const MiningProjectMap& projects) } LogPrintf("%s", warning); + + return true; } + + return false; } //! @@ -1046,10 +1055,13 @@ Researcher::Researcher() Researcher::Researcher( MiningId mining_id, MiningProjectMap projects, - const GRC::BeaconError beacon_error) + const GRC::BeaconError beacon_error, + const bool has_split_cpid + ) : m_mining_id(std::move(mining_id)) , m_projects(std::move(projects)) , m_beacon_error(beacon_error) + , m_has_split_cpid(has_split_cpid) { } @@ -1205,15 +1217,24 @@ void Researcher::Reload(MiningProjectMap projects, GRC::BeaconError beacon_error } } + bool has_split_cpid = false; + + // SplitCpid currently can occur if EITHER project have the right email but thed CPID is not converged, OR + // the email is mismatched between them or this client, or BOTH. Right now it is too hard to tease all of that + // out without significant replumbing. So instead if projects not empty run the DetectSplitCpid regardless + // of whether the mining_id has actually been populated. + if (!projects.empty()) { + has_split_cpid = DetectSplitCpid(projects); + } + if (const CpidOption cpid = mining_id.TryCpid()) { - DetectSplitCpid(projects); LogPrintf("Selected primary CPID: %s", cpid->ToString()); } else if (!projects.empty()) { LogPrintf("WARNING: no projects eligible for research rewards."); } StoreResearcher( - Researcher(std::move(mining_id), std::move(projects), beacon_error)); + Researcher(std::move(mining_id), std::move(projects), beacon_error, has_split_cpid)); } void Researcher::Refresh() @@ -1361,6 +1382,11 @@ GRC::BeaconError Researcher::BeaconError() const return m_beacon_error; } +bool Researcher::hasSplitCpid() const +{ + return m_has_split_cpid; +} + bool Researcher::ChangeMode(const ResearcherMode mode, std::string email) { email = ToLower(email); diff --git a/src/gridcoin/researcher.h b/src/gridcoin/researcher.h index c4cf6b3d5b..e4a7d5b293 100644 --- a/src/gridcoin/researcher.h +++ b/src/gridcoin/researcher.h @@ -387,11 +387,13 @@ class Researcher //! \param mining_id Represents a CPID or an investor. //! \param projects A set of local projects loaded from BOINC. //! \param beacon_error Last beacon advertisement error, if any. + //! \param has_split_cpid Existence of split cpid. //! Researcher( MiningId mining_id, MiningProjectMap projects, - const BeaconError beacon_error = GRC::BeaconError::NONE); + const BeaconError beacon_error = GRC::BeaconError::NONE, + bool has_split_cpid = false); //! //! \brief Set up the local researcher context. @@ -577,6 +579,13 @@ class Researcher //! GRC::BeaconError BeaconError() const; + //! + //! \brief Returns true if a split CPID situation exists (i.e. project list + //! refers to more than one CPID). + //! \return boolean of split cpid existence + //! + bool hasSplitCpid() const; + //! //! \brief Update how a user prefers to participate in the research reward //! protocol and set the node's BOINC account email address used to detect @@ -637,6 +646,7 @@ class Researcher MiningId m_mining_id; //!< CPID or INVESTOR variant. MiningProjectMap m_projects; //!< Local projects loaded from BOINC. GRC::BeaconError m_beacon_error; //!< Last beacon error that occurred, if any. + bool m_has_split_cpid; //!< Flag that indicates project list has more than one CPID }; // Researcher } diff --git a/src/qt/researcher/researchermodel.cpp b/src/qt/researcher/researchermodel.cpp index 2cb1f3269c..d5d8e07326 100644 --- a/src/qt/researcher/researchermodel.cpp +++ b/src/qt/researcher/researchermodel.cpp @@ -186,6 +186,11 @@ void ResearcherModel::showWizard(WalletModel* wallet_model) wizard->setStartId(ResearcherWizard::PageInvestor); } else if (detectedPoolMode()) { wizard->setStartId(ResearcherWizard::PagePoolSummary); + } else if (hasSplitCpid()) { + // If there is a split CPID situation, then the actionNeeded is also set, but + // in the case of a split CPID we want to go to the PageSummary screen, where they + // will see the warning for the split CPID. This is more important than renewing the beacon + wizard->setStartId(ResearcherWizard::PageSummary); } else if (hasRenewableBeacon()) { wizard->setStartId(ResearcherWizard::PageBeacon); } else if (!actionNeeded()) { @@ -235,7 +240,7 @@ bool ResearcherModel::actionNeeded() const } if (hasEligibleProjects()) { - return !hasActiveBeacon() && !hasPendingBeacon(); + return hasSplitCpid() || (!hasActiveBeacon() && !hasPendingBeacon()); } return !hasPoolProjects(); @@ -276,6 +281,11 @@ bool ResearcherModel::hasRAC() const return m_researcher->HasRAC(); } +bool ResearcherModel::hasSplitCpid() const +{ + return m_researcher->hasSplitCpid(); +} + bool ResearcherModel::needsBeaconAuth() const { if (!hasPendingBeacon()) { diff --git a/src/qt/researcher/researchermodel.h b/src/qt/researcher/researchermodel.h index bcc5c77346..36dfc43fb9 100644 --- a/src/qt/researcher/researchermodel.h +++ b/src/qt/researcher/researchermodel.h @@ -94,6 +94,7 @@ class ResearcherModel : public QObject bool hasRenewableBeacon() const; bool hasMagnitude() const; bool hasRAC() const; + bool hasSplitCpid() const; bool needsBeaconAuth() const; QString email() const; @@ -121,6 +122,7 @@ class ResearcherModel : public QObject bool m_configured_for_investor_mode; bool m_wizard_open; bool m_out_of_sync; + bool m_split_cpid; bool m_privacy_enabled; QString m_theme_suffix; diff --git a/src/qt/researcher/researcherwizardsummarypage.cpp b/src/qt/researcher/researcherwizardsummarypage.cpp index 1ee10b9816..8d99ac4dac 100644 --- a/src/qt/researcher/researcherwizardsummarypage.cpp +++ b/src/qt/researcher/researcherwizardsummarypage.cpp @@ -2,6 +2,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or https://opensource.org/licenses/mit-license.php. +#include "logging.h" + #include "qt/bitcoinunits.h" #include "qt/decoration.h" #include "qt/forms/ui_researcherwizardsummarypage.h" @@ -114,6 +116,7 @@ void ResearcherWizardSummaryPage::refreshOverallStatus() const int icon_size = ui->overallStatusIconLabel->width(); QString status; + QString status_tooltip; QIcon icon; if (m_researcher_model->outOfSync()) { @@ -125,6 +128,14 @@ void ResearcherWizardSummaryPage::refreshOverallStatus() } else if (m_researcher_model->hasRenewableBeacon()) { status = tr("Beacon renewal available."); icon = QIcon(":/icons/warning"); + } else if (m_researcher_model->hasSplitCpid()) { + status = tr("Split CPID or mismatched email."); + status_tooltip = tr("Your projects either refer to more than one CPID or your projects\' email do not match " + "what you used to configure Gridcoin here. Please ensure all of your projects are attached " + "using the same email address, the email address matches what was configured here, and if " + "you added a project recently, update that project and then all other projects using the " + "update button in the BOINC manager, then restart the client and recheck."); + icon = QIcon(":/icons/warning"); } else if (!m_researcher_model->hasMagnitude()) { status = tr("Waiting for magnitude."); icon = QIcon(":/icons/scraper_waiting_light"); @@ -134,6 +145,9 @@ void ResearcherWizardSummaryPage::refreshOverallStatus() } ui->overallStatusLabel->setText(status); + + ui->overallStatusLabel->setToolTip(status_tooltip); + ui->overallStatusIconLabel->setPixmap(icon.pixmap(icon_size, icon_size)); }