diff --git a/src/gridcoin/sidestake.cpp b/src/gridcoin/sidestake.cpp index 02f25fd853..de996ee234 100644 --- a/src/gridcoin/sidestake.cpp +++ b/src/gridcoin/sidestake.cpp @@ -21,6 +21,17 @@ SideStakeRegistry& GRC::GetSideStakeRegistry() return g_sidestake_entries; } +// ----------------------------------------------------------------------------- +// Class: CBitcoinAddressForStorage +// ----------------------------------------------------------------------------- +CBitcoinAddressForStorage::CBitcoinAddressForStorage() + : CBitcoinAddress() +{} + +CBitcoinAddressForStorage::CBitcoinAddressForStorage(CBitcoinAddress address) + : CBitcoinAddress(address) +{} + // ----------------------------------------------------------------------------- // Class: SideStake // ----------------------------------------------------------------------------- @@ -434,6 +445,128 @@ uint64_t SideStakeRegistry::PassivateDB() return m_sidestake_db.passivate_db(); } +void SideStakeRegistry::LoadLocalSideStakesFromConfig() +{ + std::vector vSideStakes; + std::vector> raw_vSideStakeAlloc; + double dSumAllocation = 0.0; + + // Parse destinations and allocations. We don't need to worry about any that are rejected other than a warning + // message, because any unallocated rewards will go back into the coinstake output(s). + + // If -sidestakeaddresses and -sidestakeallocations is set in either the config file or the r-w settings file + // and the settings are not empty and they are the same size, this will take precedence over the multiple entry + // -sidestake format. + std::vector addresses; + std::vector allocations; + + ParseString(gArgs.GetArg("-sidestakeaddresses", ""), ',', addresses); + ParseString(gArgs.GetArg("-sidestakeallocations", ""), ',', allocations); + + if (addresses.size() != allocations.size()) + { + LogPrintf("WARN: %s: Malformed new style sidestaking configuration entries. Reverting to original format.", + __func__); + } + + if (addresses.size() && addresses.size() == allocations.size()) + { + for (unsigned int i = 0; i < addresses.size(); ++i) + { + raw_vSideStakeAlloc.push_back(std::make_pair(addresses[i], allocations[i])); + } + } + else if (gArgs.GetArgs("-sidestake").size()) + { + for (auto const& sSubParam : gArgs.GetArgs("-sidestake")) + { + std::vector vSubParam; + + ParseString(sSubParam, ',', vSubParam); + if (vSubParam.size() != 2) + { + LogPrintf("WARN: %s: Incomplete SideStake Allocation specified. Skipping SideStake entry.", __func__); + continue; + } + + raw_vSideStakeAlloc.push_back(std::make_pair(vSubParam[0], vSubParam[1])); + } + } + + for (const auto& entry : raw_vSideStakeAlloc) + { + std::string sAddress; + double dAllocation = 0.0; + + sAddress = entry.first; + + CBitcoinAddress address(sAddress); + if (!address.IsValid()) + { + LogPrintf("WARN: %s: ignoring sidestake invalid address %s.", __func__, sAddress); + continue; + } + + if (!ParseDouble(entry.second, &dAllocation)) + { + LogPrintf("WARN: %s: Invalid allocation %s provided. Skipping allocation.", __func__, entry.second); + continue; + } + + dAllocation /= 100.0; + + if (dAllocation <= 0) + { + LogPrintf("WARN: %s: Negative or zero allocation provided. Skipping allocation.", __func__); + continue; + } + + // The below will stop allocations if someone has made a mistake and the total adds up to more than 100%. + // Note this same check is also done in SplitCoinStakeOutput, but it needs to be done here for two reasons: + // 1. Early alertment in the debug log, rather than when the first kernel is found, and 2. When the UI is + // hooked up, the SideStakeAlloc vector will be filled in by other than reading the config file and will + // skip the above code. + dSumAllocation += dAllocation; + if (dSumAllocation > 1.0) + { + LogPrintf("WARN: %s: allocation percentage over 100 percent, ending sidestake allocations.", __func__); + break; + } + + SideStake sidestake(static_cast(address), dAllocation, 0, uint256{}); + + // This will add or update (replace) a non-contract entry in the registry for the local sidestake. + NonContractAdd(sidestake); + + // This is needed because we need to detect entries in the registry map that are no longer in the config file to mark + // them deleted. + vSideStakes.push_back(sidestake); + + LogPrint(BCLog::LogFlags::MINER, "INFO: %s: SideStakeAlloc Address %s, Allocation %f", + __func__, sAddress, dAllocation); + } + + for (auto& entry : m_sidestake_entries) + { + // Only look at active entries. The others are NA for this alignment. + if (entry.second->m_status == SideStakeStatus::ACTIVE) { + auto iter = std::find(vSideStakes.begin(), vSideStakes.end(), *entry.second); + + if (iter == vSideStakes.end()) { + // Entry in map is no longer found in config files, so mark map entry deleted. + + entry.second->m_status = SideStakeStatus::DELETED; + } + } + } + + // If we get here and dSumAllocation is zero then the enablesidestaking flag was set, but no VALID distribution + // was provided in the config file, so warn in the debug log. + if (!dSumAllocation) + LogPrintf("WARN: %s: enablesidestaking was set in config but nothing has been allocated for" + " distribution!", __func__); +} + SideStakeRegistry::SideStakeDB &SideStakeRegistry::GetSideStakeDB() { return m_sidestake_db; diff --git a/src/gridcoin/sidestake.h b/src/gridcoin/sidestake.h index 59893cbaf2..c63b8ab28c 100644 --- a/src/gridcoin/sidestake.h +++ b/src/gridcoin/sidestake.h @@ -17,6 +17,9 @@ namespace GRC { class CBitcoinAddressForStorage : public CBitcoinAddress { public: + CBitcoinAddressForStorage(); + + CBitcoinAddressForStorage(CBitcoinAddress address); ADD_SERIALIZE_METHODS; @@ -419,6 +422,7 @@ class SideStakeRegistry : public IContractHandler //! //! \brief Allows local (voluntary) sidestakes to be added to the in-memory map and not persisted to //! the registry db. + //! //! \param SideStake object to add //! void NonContractAdd(SideStake& sidestake); @@ -427,6 +431,7 @@ class SideStakeRegistry : public IContractHandler //! \brief Add a sidestake entry to the registry from contract data. For the sidestake registry //! both Add and Delete actually call a common helper function AddDelete, because the action //! is actually symmetric to both. + //! //! \param ctx //! void Add(const ContractContext& ctx) override; @@ -499,6 +504,12 @@ class SideStakeRegistry : public IContractHandler //! uint64_t PassivateDB(); + //! + //! \brief This method parses the config file for local sidestakes. It is based on the original GetSideStakingStatusAndAlloc() + //! that was in miner.cpp prior to the implementation of the SideStake class. + //! + void LoadLocalSideStakesFromConfig(); + //! //! \brief A static function that is called by the scheduler to run the sidestake entry database passivation. //!