From 65081bbcb3b514535e9394ebdffee2a36abd615a Mon Sep 17 00:00:00 2001 From: "James C. Owens" Date: Sun, 20 Oct 2024 17:25:12 -0400 Subject: [PATCH] Introduce changeable magnitude weight factors for polling --- src/gridcoin/protocol.cpp | 38 ++++++++++++++++++++++++++++++++++ src/gridcoin/protocol.h | 11 ++++++++++ src/gridcoin/voting/result.cpp | 32 +++++++++++++++++++++++++--- src/test/util_tests.cpp | 12 +++++++++++ src/util.h | 16 ++++++++++++++ 5 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/gridcoin/protocol.cpp b/src/gridcoin/protocol.cpp index bd32a6838d..3b6d3a06ef 100644 --- a/src/gridcoin/protocol.cpp +++ b/src/gridcoin/protocol.cpp @@ -291,6 +291,44 @@ ProtocolEntryOption ProtocolRegistry::TryActive(const std::string& key) const return nullptr; } +ProtocolEntryOption ProtocolRegistry::TryByTimestamp(const std::string& key, const int64_t& timestamp) +{ + LOCK(cs_lock); + + // Find the latest protocol entry by key if it exists. + const auto iter = m_protocol_entries.find(key); + + // If there is no entry, return nullptr. If there is no current entry, then there is also no historical entry for the same + // key. + if (iter == m_protocol_entries.end()) { + return nullptr; + } + + // If the latest (current) active protocol entry for the given key has a timestamp before or equal to the provided timestamp then + // return the latest entry. + if (iter->second->m_timestamp <= timestamp && iter->second->m_status == ProtocolEntryStatus::ACTIVE) { + return iter->second; + } + + // Walk the revision history chain for the protocol entries with the provided key to find an active historical entry + // with a timestamp that is equal to or less than the provided timestamp, if it exists. + auto hist_protocol_entry_iter = m_protocol_db.find(iter->second->m_previous_hash); + + while (hist_protocol_entry_iter != m_protocol_db.end() + && !hist_protocol_entry_iter->second->m_previous_hash.IsNull() + && (hist_protocol_entry_iter->second->m_timestamp > timestamp + || hist_protocol_entry_iter->second->m_status != ProtocolEntryStatus::ACTIVE)) + { + hist_protocol_entry_iter = m_protocol_db.find(hist_protocol_entry_iter->second->m_previous_hash); + } + + if (hist_protocol_entry_iter == m_protocol_db.end()) { + return nullptr; + } + + return hist_protocol_entry_iter->second; +} + void ProtocolRegistry::Reset() { LOCK(cs_lock); diff --git a/src/gridcoin/protocol.h b/src/gridcoin/protocol.h index 3e8f400e17..022c149598 100644 --- a/src/gridcoin/protocol.h +++ b/src/gridcoin/protocol.h @@ -460,6 +460,17 @@ class ProtocolRegistry : public IContractHandler //! ProtocolEntryOption TryActive(const std::string& key) const; + //! + //! \brief Get the last protocol entry for the specified key string at or before the specified timestamp + //! + //! \param key The key string of the protocol entry. + //! \param timestamp The desired timestamp to limit the find. + //! + //! \return An object that either contains a reference to some protocol entry if it exists + //! for the key at or before the provided timestamp or does not. + //! + ProtocolEntryOption TryByTimestamp(const std::string& key, const int64_t& timestamp); + //! //! \brief Destroy the contract handler state in case of an error in loading //! the protocol entry registry state from LevelDB to prepare for reload from contract diff --git a/src/gridcoin/voting/result.cpp b/src/gridcoin/voting/result.cpp index 962cf21303..28faee078d 100644 --- a/src/gridcoin/voting/result.cpp +++ b/src/gridcoin/voting/result.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or https://opensource.org/licenses/mit-license.php. +#include "gridcoin/protocol.h" #include "main.h" #include "gridcoin/beacon.h" #include "gridcoin/quorum.h" @@ -787,7 +788,7 @@ class VoteCounter //! \param supply The total network money supply as of the latest //! block in the poll window in units of 1/100000000 GRC. //! - void EnableMagnitudeWeight(SuperblockPtr superblock, const uint64_t supply) + void EnableMagnitudeWeight(SuperblockPtr superblock, const uint64_t supply, const Fraction magnitude_weight_factor) { const uint64_t total_mag = superblock->m_cpids.TotalMagnitude(); @@ -796,7 +797,9 @@ class VoteCounter } // Use integer arithmetic to avoid floating-point discrepancies: - m_magnitude_factor = supply / total_mag * 100 / 567; + m_magnitude_factor = supply / total_mag + * (uint64_t) magnitude_weight_factor.GetNumerator() + / (uint64_t) magnitude_weight_factor.GetDenominator(); m_resolver.SetSuperblock(std::move(superblock)); } @@ -1133,6 +1136,28 @@ CAmount ResolveMoneySupplyForPoll(const Poll& poll) return pindex->nMoneySupply; } + +//! +//! \brief Fetch the applicable magnitude factor for the poll. This is the magnitude factor of the last entry in the +//! protocol registry database that has a timestamp at or before the poll start timestamp. +//! +//! \param poll Poll for which to fetch the Magnitude Weight Factor. +//! \return Fraction Magnitude factor expressed as a Fraction. +//! +Fraction ResolveMagnitudeWeightFactorForPoll(const Poll& poll) +{ + Fraction magnitude_weight_factor = Params().GetConsensus().DefaultMagnitudeWeightFactor; + + // Find the current protocol entry value for Magnitude Weight Factor, if it exists. + ProtocolEntryOption protocol_entry = GetProtocolRegistry().TryByTimestamp("magnitudeweightfactor", poll.m_timestamp); + + if (protocol_entry != nullptr) { + magnitude_weight_factor = Fraction().FromString(protocol_entry->m_value); + } + + return magnitude_weight_factor; +} + } // Anonymous namespace // ----------------------------------------------------------------------------- @@ -1186,7 +1211,8 @@ PollResultOption PollResult::BuildFor(const PollReference& poll_ref) if (result.m_poll.IncludesMagnitudeWeight()) { counter.EnableMagnitudeWeight( ResolveSuperblockForPoll(result.m_poll), - ResolveMoneySupplyForPoll(result.m_poll)); + ResolveMoneySupplyForPoll(result.m_poll), + ResolveMagnitudeWeightFactorForPoll(result.m_poll)); } LogPrint(BCLog::LogFlags::VOTE, "INFO: %s: number of votes = %u for poll %s", diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 1aeccd2785..1c22fb1f26 100755 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1326,6 +1326,18 @@ BOOST_AUTO_TEST_CASE(util_Fraction_Initialization_from_int64_t) BOOST_CHECK_EQUAL(fraction.IsNonNegative(), false); } +BOOST_AUTO_TEST_CASE(util_Fraction_Initialization_from_string) +{ + Fraction fraction = Fraction().FromString("100/-567"); + + BOOST_CHECK_EQUAL(fraction.GetNumerator(), -100); + BOOST_CHECK_EQUAL(fraction.GetDenominator(), 567); + BOOST_CHECK_EQUAL(fraction.IsSimplified(), true); + BOOST_CHECK_EQUAL(fraction.IsZero(), false); + BOOST_CHECK_EQUAL(fraction.IsPositive(), false); + BOOST_CHECK_EQUAL(fraction.IsNonNegative(), false); +} + BOOST_AUTO_TEST_CASE(util_Fraction_Simplify) { Fraction fraction(-4, -6); diff --git a/src/util.h b/src/util.h index b51db0661f..a1cf95490c 100644 --- a/src/util.h +++ b/src/util.h @@ -340,6 +340,22 @@ class Fraction { return strprintf("%" PRId64 "/" "%" PRId64, m_numerator, m_denominator); } + Fraction FromString(const std::string& string) const + { + std::vector string_fraction = split(string, "/"); + + int64_t numerator; + int64_t denominator; + + if (string_fraction.size() != 2 + || !ParseInt64(string_fraction[0], &numerator) + || !ParseInt64(string_fraction[1], &denominator)) { + throw std::out_of_range("Fraction input string cannot be parsed to fraction."); + } + + return Fraction(numerator, denominator, true); + } + bool operator!() { return IsZero();