diff --git a/src/gridcoin/sidestake.cpp b/src/gridcoin/sidestake.cpp index 5378af713b..5133510183 100644 --- a/src/gridcoin/sidestake.cpp +++ b/src/gridcoin/sidestake.cpp @@ -4,6 +4,7 @@ #include "sidestake.h" #include "node/ui_interface.h" +#include "univalue.h" //! //! \brief Model callback bound to the \c RwSettingsUpdated core signal. @@ -222,7 +223,7 @@ const std::vector SideStakeRegistry::ActiveSideStakeEntries() if (fEnableSideStaking) { LogPrint(BCLog::LogFlags::MINER, "INFO: %s: fEnableSideStaking = %u", __func__, fEnableSideStaking); - for (const auto& entry : m_sidestake_entries) + for (const auto& entry : m_local_sidestake_entries) { if (entry.second->m_status == SideStakeStatus::ACTIVE && allocation_sum + entry.second->m_allocation <= 1.0) { sidestakes.push_back(entry.second); @@ -234,30 +235,42 @@ const std::vector SideStakeRegistry::ActiveSideStakeEntries() return sidestakes; } -SideStakeOption SideStakeRegistry::Try(const CBitcoinAddressForStorage& key) const +std::vector SideStakeRegistry::Try(const CBitcoinAddressForStorage& key, const bool& local_only) const { LOCK(cs_lock); - const auto iter = m_sidestake_entries.find(key); + std::vector result; - if (iter == m_sidestake_entries.end()) { - return nullptr; + if (!local_only) { + const auto mandatory_entry = m_sidestake_entries.find(key); + + if (mandatory_entry != m_sidestake_entries.end()) { + result.push_back(mandatory_entry->second); + } + } + + const auto local_entry = m_local_sidestake_entries.find(key); + + if (local_entry != m_sidestake_entries.end()) { + result.push_back(local_entry->second); } - return iter->second; + return result; } -SideStakeOption SideStakeRegistry::TryActive(const CBitcoinAddressForStorage& key) const +std::vector SideStakeRegistry::TryActive(const CBitcoinAddressForStorage& key, const bool& local_only) const { LOCK(cs_lock); - if (const SideStakeOption SideStake_entry = Try(key)) { - if (SideStake_entry->m_status == SideStakeStatus::ACTIVE || SideStake_entry->m_status == SideStakeStatus::MANDATORY) { - return SideStake_entry; + std::vector result; + + for (const auto& iter : Try(key, local_only)) { + if (iter->m_status == SideStakeStatus::MANDATORY || iter->m_status == SideStakeStatus::ACTIVE) { + result.push_back(iter); } } - return nullptr; + return result; } void SideStakeRegistry::Reset() @@ -346,7 +359,9 @@ void SideStakeRegistry::AddDelete(const ContractContext& ctx) void SideStakeRegistry::NonContractAdd(SideStake& sidestake) { // Using this form of insert because we want the latest record with the same key to override any previous one. - m_sidestake_entries[sidestake.m_key] = std::make_shared(sidestake); + m_local_sidestake_entries[sidestake.m_key] = std::make_shared(sidestake); + + } void SideStakeRegistry::Add(const ContractContext& ctx) @@ -356,10 +371,10 @@ void SideStakeRegistry::Add(const ContractContext& ctx) void SideStakeRegistry::NonContractDelete(CBitcoinAddressForStorage& address) { - auto sidestake_entry_pair_iter = m_sidestake_entries.find(address); + auto sidestake_entry_pair_iter = m_local_sidestake_entries.find(address); - if (sidestake_entry_pair_iter != m_sidestake_entries.end()) { - m_sidestake_entries.erase(sidestake_entry_pair_iter); + if (sidestake_entry_pair_iter != m_local_sidestake_entries.end()) { + m_local_sidestake_entries.erase(sidestake_entry_pair_iter); } } @@ -469,9 +484,11 @@ int SideStakeRegistry::Initialize() LogPrint(LogFlags::CONTRACT, "INFO: %s: m_sidestake_db size after load: %u", __func__, m_sidestake_db.size()); LogPrint(LogFlags::CONTRACT, "INFO: %s: m_sidestake_entries size after load: %u", __func__, m_sidestake_entries.size()); - // Add the local sidestakes specified in the config file(s) to the mandatory sidestakes. + // Add the local sidestakes specified in the config file(s) to the local sidestakes map. LoadLocalSideStakesFromConfig(); + m_local_entry_already_saved_to_config = false; + return height; } @@ -497,6 +514,7 @@ void SideStakeRegistry::ResetInMemoryOnly() { LOCK(cs_lock); + m_local_sidestake_entries.clear(); m_sidestake_entries.clear(); m_sidestake_db.clear_in_memory_only(); } @@ -510,6 +528,16 @@ uint64_t SideStakeRegistry::PassivateDB() void SideStakeRegistry::LoadLocalSideStakesFromConfig() { + // If the m_local_entry_already_saved_to_config is set, then SaveLocalSideStakeToConfig was just called, + // and we want to then ignore the update signal from the r-w file change that calls this function for + // that action (only) and then reset the flag to be responsive to any changes on the core r-w file side + // through changesettings, for example. + if (m_local_entry_already_saved_to_config) { + m_local_entry_already_saved_to_config = false; + + return; + } + std::vector vSideStakes; std::vector> raw_vSideStakeAlloc; double dSumAllocation = 0.0; @@ -624,7 +652,7 @@ void SideStakeRegistry::LoadLocalSideStakesFromConfig() __func__, sAddress, dAllocation); } - for (auto& entry : m_sidestake_entries) + for (auto& entry : m_local_sidestake_entries) { // Only look at active entries. The others are NA for this alignment. if (entry.second->m_status == SideStakeStatus::ACTIVE) { @@ -645,6 +673,42 @@ void SideStakeRegistry::LoadLocalSideStakesFromConfig() " distribution!", __func__); } +bool SideStakeRegistry::SaveLocalSideStakesToConfig() +{ + bool status = false; + + std::string addresses; + std::string allocations; + std::string descriptions; + + std::string separator; + + std::vector> settings; + + unsigned int i = 0; + for (const auto& iter : m_local_sidestake_entries) { + if (i) { + separator = ","; + } + + addresses += separator + iter.second->m_key.ToString(); + allocations += separator + ToString(iter.second->m_allocation * 100.0); + descriptions += separator + iter.second->m_description; + + ++i; + } + + settings.push_back(std::make_pair("addresses", addresses)); + settings.push_back(std::make_pair("allocations", allocations)); + settings.push_back(std::make_pair("descriptions", descriptions)); + + status = updateRwSettings(settings); + + m_local_entry_already_saved_to_config = true; + + return status; +} + void SideStakeRegistry::SubscribeToCoreSignals() { uiInterface.RwSettingsUpdated_connect(std::bind(RwSettingsUpdated, this)); diff --git a/src/gridcoin/sidestake.h b/src/gridcoin/sidestake.h index afebd8faed..964f933b5c 100644 --- a/src/gridcoin/sidestake.h +++ b/src/gridcoin/sidestake.h @@ -197,7 +197,7 @@ typedef std::shared_ptr SideStake_ptr; //! //! \brief A type that either points to some sidestake or does not. //! -typedef const SideStake_ptr SideStakeOption; +//typedef const SideStake_ptr SideStakeOption; //! //! \brief The body of a sidestake entry contract. Note that this body is bimodal. It @@ -416,21 +416,24 @@ class SideStakeRegistry : public IContractHandler //! \brief Get the current sidestake entry for the specified key string. //! //! \param key The key string of the sidestake entry. + //! \param local_only If true causes Try to only check the local sidestake map. Defaults to false. //! - //! \return An object that either contains a reference to some sidestake entry if it exists - //! for the key or does not. + //! \return A vector of smart pointers to entries matching the provided key (address). Up to two elements + //! are returned, mandatory entry first, unless local only boolean is set true. //! - SideStakeOption Try(const CBitcoinAddressForStorage& key) const; + std::vector Try(const CBitcoinAddressForStorage& key, const bool& local_only = false) const; //! //! \brief Get the current sidestake entry for the specified key string if it has a status of ACTIVE or MANDATORY. //! //! \param key The key string of the sidestake entry. + //! \param local_only If true causes Try to only check the local sidestake map. Defaults to false. //! - //! \return An object that either contains a reference to some sidestake entry if it exists - //! for the key and is in the required status or does not. + //! \return A vector of smart pointers to entries matching the provided key (address) that are in status of + //! MANDATORY or ACTIVE. Up to two elements are returned, mandatory entry first, unless local only boolean + //! is set true. //! - SideStakeOption TryActive(const CBitcoinAddressForStorage& key) const; + std::vector TryActive(const CBitcoinAddressForStorage& key, const bool& local_only = false) const; //! //! \brief Destroy the contract handler state in case of an error in loading @@ -584,14 +587,25 @@ class SideStakeRegistry : public IContractHandler //! void AddDelete(const ContractContext& ctx); + //! + //! \brief Private helper function for non-contract add and delete to align the config r-w file with + //! in memory local sidestake map. + //! + //! \return bool true if successful. + //! + bool SaveLocalSideStakesToConfig(); + void SubscribeToCoreSignals(); void UnsubscribeFromCoreSignals(); - SideStakeMap m_sidestake_entries; //!< Contains the current sidestake entries including entries marked DELETED. + SideStakeMap m_local_sidestake_entries; //!< Contains the local (non-contract) sidestake entries. + SideStakeMap m_sidestake_entries; //!< Contains the mandatory sidestake entries, including DELETED. PendingSideStakeMap m_pending_sidestake_entries {}; //!< Not used. Only to satisfy the template. SideStakeDB m_sidestake_db; + bool m_local_entry_already_saved_to_config = false; //!< Flag to prevent reload on signal if individual entry saved already. + public: SideStakeDB& GetSideStakeDB(); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index f7b9123a3f..373ba3e98b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -98,8 +98,6 @@ UniValue getstakinginfo(const UniValue& params, bool fHelp) bool fEnableSideStaking = gArgs.GetBoolArg("-enablesidestaking"); - if (fEnableSideStaking) vSideStakeAlloc = GRC::GetSideStakeRegistry().ActiveSideStakeEntries(); - stakesplitting.pushKV("stake-splitting-enabled", fEnableStakeSplit); if (fEnableStakeSplit) { @@ -110,6 +108,8 @@ UniValue getstakinginfo(const UniValue& params, bool fHelp) } obj.pushKV("stake-splitting", stakesplitting); + vSideStakeAlloc = GRC::GetSideStakeRegistry().ActiveSideStakeEntries(); + sidestaking.pushKV("local_side_staking_enabled", fEnableSideStaking); // Note that if local_side_staking_enabled is true, then local sidestakes will be applicable and shown. Mandatory