diff --git a/src/main.cpp b/src/main.cpp index 64c68843d2..26119b2da9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -155,6 +155,10 @@ bool fUseFastIndex = false; std::map mvTimers; // Contains event timers that reset after max ms duration iterator is exceeded +// Temporary block version 11 transition helpers: +int64_t g_v11_timestamp = 0; +int64_t g_v11_legacy_beacon_days = 14; + // End of Gridcoin Global vars ////////////////////////////////////////////////////////////////////////////// @@ -3752,6 +3756,12 @@ bool GridcoinServices() // legacy transactions that cannot validate: // mempool.DiscardVersion1(); + + // Set the timestamp for the block version 11 threshold. This + // is temporary. Remove this variable in a release that comes + // after the hard fork. + // + g_v11_timestamp = pindexBest->nTime; } //Dont perform the following functions if out of sync @@ -4087,6 +4097,15 @@ bool LoadBlockIndex(bool fAllowNew) nNewIndex2 = 36500; //1-24-2016 MAX_OUTBOUND_CONNECTIONS = (int)GetArg("-maxoutboundconnections", 8); + + // Temporary transition to version 2 beacons after the block version 11 + // hard-fork: + // + try { + g_v11_legacy_beacon_days = std::stoi(GetArg("-v11beacondays", "")); + } catch (...) { + g_v11_legacy_beacon_days = 365 * 100; // Big number that won't wrap. + } } LogPrintf("Mode=%s", fTestNet ? "TestNet" : "Prod"); diff --git a/src/neuralnet/beacon.cpp b/src/neuralnet/beacon.cpp index 4051201bca..c804afd9d9 100644 --- a/src/neuralnet/beacon.cpp +++ b/src/neuralnet/beacon.cpp @@ -9,6 +9,9 @@ using namespace NN; using LogFlags = BCLog::LogFlags; +extern int64_t g_v11_timestamp; +extern int64_t g_v11_legacy_beacon_days; + namespace { BeaconRegistry g_beacons; @@ -130,7 +133,18 @@ int64_t Beacon::Age(const int64_t now) const bool Beacon::Expired(const int64_t now) const { - return Age(now) > MAX_AGE; + if (Age(now) > MAX_AGE) { + return true; + } + + // Temporary transition to version 2 beacons after the block version 11 + // hard-fork: + // + if (m_timestamp <= g_v11_timestamp) { + return now - g_v11_timestamp > g_v11_legacy_beacon_days * 86400; + } + + return false; } bool Beacon::Renewable(const int64_t now) const @@ -374,6 +388,11 @@ bool BeaconRegistry::Validate(const Contract& contract) const const auto payload = contract.SharePayloadAs(); + if (payload->m_version < 2) { + LogPrint(LogFlags::CONTRACT, "%s: Legacy beacon contract", __func__); + return false; + } + if (!payload->WellFormed(contract.m_action.Value())) { LogPrint(LogFlags::CONTRACT, "%s: Malformed beacon contract", __func__); return false; @@ -393,7 +412,12 @@ bool BeaconRegistry::Validate(const Contract& contract) const // Self-service beacon removal allowed when the signature matches the key // of the original beacon: if (contract.m_action == ContractAction::REMOVE) { - return current_beacon->m_public_key == payload->m_beacon.m_public_key; + if (current_beacon->m_public_key != payload->m_beacon.m_public_key) { + LogPrint(LogFlags::CONTRACT, "%s: Beacon key mismatch", __func__); + return false; + } + + return true; } // Self-service beacon replacement will be authenticated by the scrapers: @@ -401,6 +425,13 @@ bool BeaconRegistry::Validate(const Contract& contract) const return true; } + // Transition to version 2 beacons after the block version 11 threshold. + // Legacy beacons are not renewable: + if (current_beacon->m_timestamp <= g_v11_timestamp) { + LogPrint(LogFlags::CONTRACT, "%s: Can't renew legacy beacon", __func__); + return false; + } + if (!current_beacon->Renewable(contract.m_tx_timestamp)) { LogPrint(LogFlags::CONTRACT, "%s: Beacon for CPID %s is not renewable. Age: %" PRId64, diff --git a/src/neuralnet/researcher.cpp b/src/neuralnet/researcher.cpp index 71b7fbde4c..b7d5d5f43a 100644 --- a/src/neuralnet/researcher.cpp +++ b/src/neuralnet/researcher.cpp @@ -29,6 +29,7 @@ std::string ExtractXML(const std::string& XMLdata, const std::string& key, const extern CCriticalSection cs_main; extern std::string msMiningErrors; +extern int64_t g_v11_timestamp; namespace { //! @@ -1225,7 +1226,7 @@ bool Researcher::UpdateEmail(std::string email) return true; } -AdvertiseBeaconResult Researcher::AdvertiseBeacon() +AdvertiseBeaconResult Researcher::AdvertiseBeacon(const bool force) { const CpidOption cpid = m_mining_id.TryCpid(); @@ -1240,15 +1241,21 @@ AdvertiseBeaconResult Researcher::AdvertiseBeacon() AdvertiseBeaconResult result(BeaconError::NONE); - if (!current_beacon) { - if (g_recent_beacons.Try(*cpid)) { - LogPrintf("%s: Beacon awaiting confirmation already", __func__); - return BeaconError::PENDING; - } - + if (force) { + result = SendNewBeacon(*cpid); + } else if (g_recent_beacons.Try(*cpid)) { + LogPrintf("%s: Beacon awaiting confirmation already", __func__); + return BeaconError::PENDING; + } else if (!current_beacon) { result = SendNewBeacon(*cpid); } else { - result = RenewBeacon(*cpid, *current_beacon); + // Temporary transition to version 2 beacons after the block version 11 + // threshold. Legacy beacons are not renewable: + if (current_beacon->m_timestamp <= g_v11_timestamp) { + result = SendNewBeacon(*cpid); + } else { + result = RenewBeacon(*cpid, *current_beacon); + } } if (result.Error() == BeaconError::NONE) { diff --git a/src/neuralnet/researcher.h b/src/neuralnet/researcher.h index 6dbc618888..772a44c034 100644 --- a/src/neuralnet/researcher.h +++ b/src/neuralnet/researcher.h @@ -512,10 +512,17 @@ class Researcher //! - The wallet is fully unlocked //! - The wallet contains a balance sufficient to send a transaction //! + //! The \p force parameter instructs the wallet to generate a new beacon + //! private key even when a valid beacon exists for the current CPID. It + //! allows a user to send a beacon to recover the claim to their CPID if + //! they lost the original private key. + //! + //! \param force Ignore active and pending beacons for the current CPID. + //! //! \return A variant that contains the new public key if successful or a //! description of the error that occurred. //! - AdvertiseBeaconResult AdvertiseBeacon(); + AdvertiseBeaconResult AdvertiseBeacon(const bool force = false); //! //! \brief Submit a contract to the network to revoke an existing beacon. diff --git a/src/neuralnet/tally.cpp b/src/neuralnet/tally.cpp index eed39c1b87..dd9041d067 100644 --- a/src/neuralnet/tally.cpp +++ b/src/neuralnet/tally.cpp @@ -15,6 +15,8 @@ using namespace NN; using LogFlags = BCLog::LogFlags; +extern int64_t g_v11_timestamp; + namespace { //! //! \brief Set the correct CPID from the block claim when the block index @@ -173,6 +175,13 @@ class ResearcherTally for (; pindex; pindex = pindex->pnext) { if (pindex->nHeight + 1 == GetV11Threshold()) { + // Set the timestamp for the block version 11 threshold. This + // is temporary. Remove this variable in a release that comes + // after the hard fork. For now, this is the least cumbersome + // place to set the value: + // + g_v11_timestamp = pindex->nTime; + // This will finish loading the research accounting context // for snapshot accrual (block version 11+): return ActivateSnapshotAccrual(pindex, current_superblock); diff --git a/src/qt/forms/researcherwizardbeaconpage.ui b/src/qt/forms/researcherwizardbeaconpage.ui index 09c6d22cce..60809c34eb 100644 --- a/src/qt/forms/researcherwizardbeaconpage.ui +++ b/src/qt/forms/researcherwizardbeaconpage.ui @@ -161,6 +161,23 @@ + + + + + 10 + 75 + true + + + + font-weight: bold; + + + All active beacon holders must send a new beacon for the protocol update. + + + diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 46d7f931e8..e6d523d960 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -223,6 +223,7 @@ void OverviewPage::setResearcherModel(ResearcherModel *researcherModel) updateResearcherStatus(); connect(researcherModel, SIGNAL(researcherChanged()), this, SLOT(updateResearcherStatus())); + connect(researcherModel, SIGNAL(magnitudeChanged()), this, SLOT(updateMagnitude())); connect(researcherModel, SIGNAL(accrualChanged()), this, SLOT(updatePendingAccrual())); connect(researcherModel, SIGNAL(beaconChanged()), this, SLOT(updateResearcherAlert())); connect(ui->beaconButton, SIGNAL(clicked()), this, SLOT(onBeaconButtonClicked())); @@ -279,12 +280,21 @@ void OverviewPage::updateResearcherStatus() ui->statusLabel->setText(researcherModel->formatStatus()); ui->cpidLabel->setText(researcherModel->formatCpid()); - ui->magnitudeLabel->setText(researcherModel->formatMagnitude()); + updateMagnitude(); updatePendingAccrual(); updateResearcherAlert(); } +void OverviewPage::updateMagnitude() +{ + if (!researcherModel) { + return; + } + + ui->magnitudeLabel->setText(researcherModel->formatMagnitude()); +} + void OverviewPage::updatePendingAccrual() { if (!researcherModel) { diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index a2b982eaa0..f3e131f9a8 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -63,6 +63,7 @@ public slots: private slots: void updateDisplayUnit(); void updateResearcherStatus(); + void updateMagnitude(); void updatePendingAccrual(); void updateResearcherAlert(); void onBeaconButtonClicked(); diff --git a/src/qt/researcher/researchermodel.cpp b/src/qt/researcher/researchermodel.cpp index 1ea27c37d2..27dab60bb4 100644 --- a/src/qt/researcher/researchermodel.cpp +++ b/src/qt/researcher/researchermodel.cpp @@ -20,6 +20,8 @@ using namespace NN; using LogFlags = BCLog::LogFlags; +extern int64_t g_v11_timestamp; + namespace { constexpr double SECONDS_IN_DAY = 24.0 * 60.0 * 60.0; constexpr int64_t BEACON_RENEWAL_WARNING_AGE = Beacon::MAX_AGE - (15 * SECONDS_IN_DAY); @@ -187,7 +189,7 @@ void ResearcherModel::showWizard(WalletModel* wallet_model) return; } - if (hasRenewableBeacon()) { + if (hasRenewableBeacon() || needsV2BeaconUpgrade()) { wizard->setStartId(ResearcherWizard::PageBeacon); } else if (!actionNeeded()) { wizard->setStartId(ResearcherWizard::PageSummary); @@ -208,7 +210,8 @@ bool ResearcherModel::actionNeeded() const } return !hasEligibleProjects() - || (!hasActiveBeacon() && !hasPendingBeacon()); + || (!hasActiveBeacon() && !hasPendingBeacon()) + || needsV2BeaconUpgrade(); } bool ResearcherModel::hasEligibleProjects() const @@ -238,9 +241,22 @@ bool ResearcherModel::hasMagnitude() const bool ResearcherModel::needsBeaconAuth() const { - return !hasRenewableBeacon() - && hasPendingBeacon() - && IsV11Enabled(nBestHeight + 1); + if (!hasPendingBeacon() || !IsV11Enabled(nBestHeight + 1)) { + return false; + } + + if (!hasActiveBeacon()) { + return true; + } + + return m_beacon->m_public_key != m_pending_beacon->m_public_key; +} + +bool ResearcherModel::needsV2BeaconUpgrade() const +{ + return m_beacon + && m_beacon->m_timestamp <= g_v11_timestamp + && (!m_pending_beacon || m_pending_beacon->m_timestamp <= g_v11_timestamp); } QString ResearcherModel::email() const @@ -437,6 +453,7 @@ void ResearcherModel::refresh() { updateBeacon(); + emit magnitudeChanged(); emit accrualChanged(); } diff --git a/src/qt/researcher/researchermodel.h b/src/qt/researcher/researchermodel.h index 9d5c694c1c..02a9566f68 100644 --- a/src/qt/researcher/researchermodel.h +++ b/src/qt/researcher/researchermodel.h @@ -85,6 +85,7 @@ class ResearcherModel : public QObject bool hasRenewableBeacon() const; bool hasMagnitude() const; bool needsBeaconAuth() const; + bool needsV2BeaconUpgrade() const; QString email() const; QString formatCpid() const; @@ -117,6 +118,7 @@ class ResearcherModel : public QObject signals: void researcherChanged(); void beaconChanged(); + void magnitudeChanged(); void accrualChanged(); public slots: diff --git a/src/qt/researcher/researcherwizardbeaconpage.cpp b/src/qt/researcher/researcherwizardbeaconpage.cpp index 6e2b8694ad..37c4bc6e65 100644 --- a/src/qt/researcher/researcherwizardbeaconpage.cpp +++ b/src/qt/researcher/researcherwizardbeaconpage.cpp @@ -77,6 +77,7 @@ void ResearcherWizardBeaconPage::refresh() ui->cpidLabel->setText(m_researcher_model->formatCpid()); ui->sendBeaconButton->setVisible(isEnabled()); + ui->v2BeaconNoticeLabel->setVisible(m_researcher_model->needsV2BeaconUpgrade()); ui->continuePromptWrapper->setVisible(!isEnabled()); emit completeChanged(); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 7e6ca9d824..cd0a52a658 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -605,17 +605,29 @@ UniValue rainbymagnitude(const UniValue& params, bool fHelp) UniValue advertisebeacon(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 0) + if (fHelp || params.size() > 1) throw runtime_error( - "advertisebeacon\n" + "advertisebeacon ( force )\n" + "\n" + "[force] --> If true, generate new beacon keys and send a new " + "beacon even when an active or pending beacon exists for your " + "CPID. This is useful if you lose a wallet with your original " + "beacon keys but not necessary otherwise.\n" "\n" "Advertise a beacon (Requires wallet to be fully unlocked)\n"); EnsureWalletIsUnlocked(); + const bool force = params.size() >= 1 ? params[0].get_bool() : false; + LOCK2(cs_main, pwalletMain->cs_wallet); - NN::AdvertiseBeaconResult result = NN::Researcher::Get()->AdvertiseBeacon(); + if (force && !IsV11Enabled(nBestHeight + 1)) { + throw JSONRPCError(RPC_INVALID_REQUEST, + "force not available until block " + std::to_string(GetV11Threshold())); + } + + NN::AdvertiseBeaconResult result = NN::Researcher::Get()->AdvertiseBeacon(force); if (auto public_key_option = result.TryPublicKey()) { const NN::Beacon beacon(std::move(*public_key_option)); diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 67edc69e4e..dfeda38f61 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -160,6 +160,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "walletpassphrase" , 2 }, // Mining + { "advertisebeacon" , 0 }, { "superblocks" , 0 }, { "superblocks" , 1 }, diff --git a/src/test/neuralnet/beacon_tests.cpp b/src/test/neuralnet/beacon_tests.cpp index b5cb829b31..ca2bc563b5 100644 --- a/src/test/neuralnet/beacon_tests.cpp +++ b/src/test/neuralnet/beacon_tests.cpp @@ -164,11 +164,11 @@ BOOST_AUTO_TEST_CASE(it_determines_whether_a_beacon_expired) const NN::Beacon valid(CPubKey(), NN::Beacon::MAX_AGE); BOOST_CHECK(valid.Expired(NN::Beacon::MAX_AGE) == false); - const NN::Beacon almost(CPubKey(), 0); - BOOST_CHECK(almost.Expired(NN::Beacon::MAX_AGE) == false); + const NN::Beacon almost(CPubKey(), 1); + BOOST_CHECK(almost.Expired(NN::Beacon::MAX_AGE + 1) == false); - const NN::Beacon expired(CPubKey(), 0); - BOOST_CHECK(expired.Expired(NN::Beacon::MAX_AGE + 1) == true); + const NN::Beacon expired(CPubKey(), 1); + BOOST_CHECK(expired.Expired(NN::Beacon::MAX_AGE + 2) == true); } BOOST_AUTO_TEST_CASE(it_determines_whether_a_beacon_is_renewable) @@ -176,11 +176,11 @@ BOOST_AUTO_TEST_CASE(it_determines_whether_a_beacon_is_renewable) const NN::Beacon not_needed(CPubKey(), NN::Beacon::RENEWAL_AGE); BOOST_CHECK(not_needed.Renewable(NN::Beacon::RENEWAL_AGE) == false); - const NN::Beacon almost(CPubKey(), 0); + const NN::Beacon almost(CPubKey(), 1); BOOST_CHECK(almost.Renewable(NN::Beacon::RENEWAL_AGE) == false); - const NN::Beacon renewable(CPubKey(), 0); - BOOST_CHECK(renewable.Renewable(NN::Beacon::RENEWAL_AGE + 1) == true); + const NN::Beacon renewable(CPubKey(), 1); + BOOST_CHECK(renewable.Renewable(NN::Beacon::RENEWAL_AGE + 2) == true); } BOOST_AUTO_TEST_CASE(it_produces_a_key_id)