diff --git a/src/gridcoin/accrual/snapshot.h b/src/gridcoin/accrual/snapshot.h index b54549b5d7..3b27f5e13f 100644 --- a/src/gridcoin/accrual/snapshot.h +++ b/src/gridcoin/accrual/snapshot.h @@ -13,7 +13,10 @@ #include "gridcoin/accrual/computer.h" #include "gridcoin/beacon.h" #include "gridcoin/cpid.h" +#include "gridcoin/protocol.h" +#include "gridcoin/sidestake.h" #include "gridcoin/superblock.h" +#include "gridcoin/support/block_finder.h" #include "gridcoin/support/filehash.h" #include "node/blockstorage.h" #include "serialize.h" @@ -29,6 +32,7 @@ namespace { using namespace GRC; using LogFlags = BCLog::LogFlags; +/* //! //! \brief Numerator of the static magnitude unit coefficient for snapshot //! accrual (block version 11 and greater). @@ -40,6 +44,7 @@ constexpr int64_t MAG_UNIT_NUMERATOR = 1; //! accrual (block version 11 and greater). //! constexpr int64_t MAG_UNIT_DENOMINATOR = 4; +*/ //! //! \brief Calculates the current accrual for a CPID by adding the snapshot of @@ -61,6 +66,66 @@ class SnapshotCalculator { } + //! + //! \brief Returns the current magnitude unit allocation fraction as of the provided superblock. Prior to block v13 this is + //! fixed at 1/4. + //! + //! The magnitude unit here will only change along superblock boundaries. Whatever the active value of the magnitude unit is + //! at the superblock provided will be used for accruals. + //! + //! \return Allocation Fraction representing Magnitude Unit. + //! + Allocation GetMagnitudeUnit() const + { + Allocation magnitude_unit = Params().GetConsensus().DefaultMagnitudeUnit; + + // Before V13 magnitude unit is 1/4. + if (!IsV13Enabled(m_superblock.m_height)) { + return Allocation(1, 4); + } + + // Find the current protocol entry value for Magnitude Weight Factor, if it exists. + ProtocolEntryOption protocol_entry = GetProtocolRegistry().TryLastBeforeTimestamp("magnitudeunit", m_superblock.m_timestamp); + + // If their is an entry prior or equal in timestamp to the superblock and it is active then set the magnitude unit + // to that value. If the last entry is not active (i.e. deleted), then leave at the default. + if (protocol_entry != nullptr && protocol_entry->m_status == ProtocolEntryStatus::ACTIVE) { + magnitude_unit = Fraction().FromString(protocol_entry->m_value); + } + + return magnitude_unit; + } + + //! + //! \brief Static version of GetMagnitudeUnit used in Tally class. + //! + //! \param index for which to return the current magnitude unit. + //! + //! \return Allocation fraction representing magnitude unit. + //! + static Allocation GetMagnitudeUnit(CBlockIndex* index) + { + CBlockIndex* sb_index = BlockFinder::FindLatestSuperblock(index); + + Allocation magnitude_unit = Params().GetConsensus().DefaultMagnitudeUnit; + + // Before V13 magnitude unit is 1/4. + if (!IsV13Enabled(sb_index->nHeight)) { + return Allocation(1, 4); + } + + // Find the current protocol entry value for Magnitude Weight Factor, if it exists. + ProtocolEntryOption protocol_entry = GetProtocolRegistry().TryLastBeforeTimestamp("magnitudeunit", sb_index->GetBlockTime()); + + // If their is an entry prior or equal in timestamp to the superblock and it is active then set the magnitude unit + // to that value. If the last entry is not active (i.e. deleted), then leave at the default. + if (protocol_entry != nullptr && protocol_entry->m_status == ProtocolEntryStatus::ACTIVE) { + magnitude_unit = Fraction().FromString(protocol_entry->m_value); + } + + return magnitude_unit; + } + //! //! \brief Get the magnitude unit factored into the reward calculation. //! @@ -72,7 +137,7 @@ class SnapshotCalculator //! //! \return Amount paid per unit of magnitude per day in units of GRC. //! - static double MagnitudeUnit() + double MagnitudeUnit() const { // Superblock-based accrual calculations do not rely on the rolling // two-week network payment average. Instead, we calculate research @@ -80,7 +145,7 @@ class SnapshotCalculator // quantity of the formula used to determine the magnitude unit for // the legacy research age accrual calculations. // - // Where... + // Where (prior to block v13) ... // // blocks_per_day = 960 // grc_per_block = 50 @@ -95,7 +160,8 @@ class SnapshotCalculator // // ...rounded-up to 0.25: // - return static_cast(MAG_UNIT_NUMERATOR) / MAG_UNIT_DENOMINATOR; + // V13+, the magnitude unit can be set by protocol entry. + return GetMagnitudeUnit().ToDouble(); } //! @@ -142,7 +208,7 @@ class SnapshotCalculator // const uint64_t base_accrual = accrual_timespan * CurrentMagnitude(cpid).Scaled() - * MAG_UNIT_NUMERATOR; + * GetMagnitudeUnit().GetNumerator(); // If the accrual calculation will overflow a 64-bit integer, we need // more bits. Arithmetic with the big integer type is much slower and @@ -155,7 +221,7 @@ class SnapshotCalculator accrual_bn *= COIN; accrual_bn /= 86400; accrual_bn /= Magnitude::SCALE_FACTOR; - accrual_bn /= MAG_UNIT_DENOMINATOR; + accrual_bn /= GetMagnitudeUnit().GetDenominator(); accrual = accrual_bn.GetLow64(); } else { @@ -163,7 +229,7 @@ class SnapshotCalculator * COIN / 86400 / Magnitude::SCALE_FACTOR - / MAG_UNIT_DENOMINATOR; + / GetMagnitudeUnit().GetDenominator(); } LogPrint(BCLog::LogFlags::ACCRUAL, "INFO %s: CPID = %s, LastRewardHeight() = %u, accrual_timespan = %" PRId64 ", " @@ -263,7 +329,7 @@ class SnapshotAccrualComputer : public IAccrualComputer, SnapshotCalculator // the amount of accrual that a CPID can collect over two days when the // CPID achieves the maximum magnitude value supported in a superblock. // - // Where... + // Where... (for block versions below V13) // // max_magnitude = 32767 // magnitude_unit = 0.25 @@ -272,9 +338,10 @@ class SnapshotAccrualComputer : public IAccrualComputer, SnapshotCalculator // // max_magnitude * magnitude_unit * 2 = max_accrual = 16383.5 // - // ...rounded-up to: + // ...rounded-up to 16384. // - return 16384 * COIN; + // For V13+, the magnitude unit can be set by protocol entry. + return (GetMagnitudeUnit() * 32768 * 2 * COIN).ToCAmount(); } //! @@ -290,7 +357,7 @@ class SnapshotAccrualComputer : public IAccrualComputer, SnapshotCalculator //! double MagnitudeUnit() const override { - return SnapshotCalculator::MagnitudeUnit(); + return GetMagnitudeUnit().ToDouble(); } int64_t AccrualAge() const override diff --git a/src/gridcoin/support/block_finder.cpp b/src/gridcoin/support/block_finder.cpp index c31d5b06e1..6cea9a97ea 100644 --- a/src/gridcoin/support/block_finder.cpp +++ b/src/gridcoin/support/block_finder.cpp @@ -57,11 +57,13 @@ CBlockIndex* BlockFinder::FindByMinTime(int64_t time) } // The arguments are passed by value on purpose. -CBlockIndex* BlockFinder::FindByMinTimeFromGivenIndex(int64_t time, CBlockIndex* index) +CBlockIndex* BlockFinder::FindByMinTimeFromGivenIndex(int64_t time, CBlockIndex* const index_in) { + CBlockIndex* index = index_in; + // If no starting index is provided (i.e. second parameter is omitted or nullptr is passed in, // then start at the Genesis Block. This is in general expensive and should be avoided. - if (!index) { + if (index == nullptr) { index = pindexGenesisBlock; } @@ -71,3 +73,23 @@ CBlockIndex* BlockFinder::FindByMinTimeFromGivenIndex(int64_t time, CBlockIndex* return index; } + +CBlockIndex* BlockFinder::FindLatestSuperblock(CBlockIndex* const index_in) +{ + CBlockIndex* index = index_in; + + // If no input index is provided, start at the head of the chain. + if (index == nullptr) { + index = pindexBest; + } + + while (index && index->pprev) { + if (index->IsSuperblock()) { + break; + } + + index = index->pprev; + } + + return index; +} diff --git a/src/gridcoin/support/block_finder.h b/src/gridcoin/support/block_finder.h index 9d7ea552ef..4401f3852e 100644 --- a/src/gridcoin/support/block_finder.h +++ b/src/gridcoin/support/block_finder.h @@ -48,7 +48,14 @@ class BlockFinder //! \return CBlockIndex pointing to the youngest block which is not older than \p time, or //! the head of the chain if it is older than \p time. //! - static CBlockIndex* FindByMinTimeFromGivenIndex(int64_t time, CBlockIndex* index = nullptr); + static CBlockIndex* FindByMinTimeFromGivenIndex(int64_t time, CBlockIndex* const index_in = nullptr); + + //! + //! \brief Find the last superblock at or before the provided index. + //! \param CBlockIndex from where to start. + //! \return CBlockIndex pointing to the block containing the last superblock contract. + //! + static CBlockIndex* FindLatestSuperblock(CBlockIndex * const index_in = nullptr); }; } // namespace GRC diff --git a/src/gridcoin/tally.cpp b/src/gridcoin/tally.cpp index 0f92700364..48c6da144b 100644 --- a/src/gridcoin/tally.cpp +++ b/src/gridcoin/tally.cpp @@ -1063,10 +1063,10 @@ CAmount Tally::MaxEmission(const int64_t payment_time) return NetworkTally::MaxEmission(payment_time) * COIN; } -double Tally::GetMagnitudeUnit(const CBlockIndex* const pindex) +double Tally::GetMagnitudeUnit(CBlockIndex* const pindex) { if (pindex->nVersion >= 11) { - return SnapshotCalculator::MagnitudeUnit(); + return SnapshotCalculator::GetMagnitudeUnit(pindex).ToDouble(); } return g_network_tally.GetMagnitudeUnit(pindex->nTime); @@ -1158,7 +1158,9 @@ CAmount Tally::GetNewbieSuperblockAccrualCorrection(const Cpid& cpid, const Supe const auto tally_accrual_period = [&]( const int64_t low_time, const int64_t high_time, - const GRC::Magnitude magnitude) + const GRC::Magnitude magnitude, + const Allocation magnitude_unit, + CAmount max_reward) { int64_t time_interval = high_time - low_time; @@ -1175,7 +1177,7 @@ CAmount Tally::GetNewbieSuperblockAccrualCorrection(const Cpid& cpid, const Supe // correctly in the bignumber calculations. const uint64_t base_accrual = abs_time_interval * magnitude.Scaled() - * MAG_UNIT_NUMERATOR; + * magnitude_unit.GetNumerator(); int64_t period = 0; @@ -1184,7 +1186,7 @@ CAmount Tally::GetNewbieSuperblockAccrualCorrection(const Cpid& cpid, const Supe accrual_bn *= COIN; accrual_bn /= 86400; accrual_bn /= Magnitude::SCALE_FACTOR; - accrual_bn /= MAG_UNIT_DENOMINATOR; + accrual_bn /= magnitude_unit.GetDenominator(); period = accrual_bn.GetLow64() * (int64_t) sign; } @@ -1194,14 +1196,11 @@ CAmount Tally::GetNewbieSuperblockAccrualCorrection(const Cpid& cpid, const Supe * COIN / 86400 / Magnitude::SCALE_FACTOR - / MAG_UNIT_DENOMINATOR; + / magnitude_unit.GetDenominator(); } accrual += period; - // TODO: Change this to refer to MaxReward() from the snapshot computer. - int64_t max_reward = 16384 * COIN; - if (accrual > max_reward) { int64_t overage = accrual - max_reward; @@ -1267,7 +1266,12 @@ CAmount Tally::GetNewbieSuperblockAccrualCorrection(const Cpid& cpid, const Supe // Stop the accrual when we get to a superblock that is before the beacon advertisement. if (pindex->nTime < beacon_ptr->m_timestamp) break; - CAmount period = tally_accrual_period(pindex->nTime, pindex_high->nTime, magnitude); + // We are only going to use the accrual computer here for the magnitude unit and max_reward. + AccrualComputer computer = GetSnapshotComputer(cpid, pindex->GetBlockTime(), pindex); + + int64_t max_reward = computer->MaxReward(); + + CAmount period = tally_accrual_period(pindex->nTime, pindex_high->nTime, magnitude, computer->MagnitudeUnit(), max_reward); LogPrint(BCLog::LogFlags::ACCRUAL, "INFO %s: period_num = %u, " "low height = %i, high height = %u, magnitude at low height SB = %f, " diff --git a/src/gridcoin/tally.h b/src/gridcoin/tally.h index ed4b6c6b0a..5c73555dbb 100644 --- a/src/gridcoin/tally.h +++ b/src/gridcoin/tally.h @@ -99,7 +99,7 @@ class Tally //! //! \return Current magnitude unit adjusted for the specified block. //! - static double GetMagnitudeUnit(const CBlockIndex* const pindex); + static double GetMagnitudeUnit(CBlockIndex * const pindex); //! //! \brief Get a traversable object for the research accounts stored in diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 69d73a2a39..933c4fac43 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -57,7 +57,7 @@ UniValue MRCToJson(const GRC::MRC& mrc) { return json; } -UniValue ClaimToJson(const GRC::Claim& claim, const CBlockIndex* const pindex) +UniValue ClaimToJson(const GRC::Claim& claim, CBlockIndex* pindex) { UniValue json(UniValue::VOBJ); @@ -155,7 +155,7 @@ UniValue SuperblockToJson(const GRC::SuperblockPtr& superblock) return SuperblockToJson(*superblock); } -UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool fPrintTransactionDetail) +UniValue blockToJSON(const CBlock& block, CBlockIndex* blockindex, bool fPrintTransactionDetail) { UniValue result(UniValue::VOBJ); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 23c318c659..adc094b752 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -375,7 +375,9 @@ UniValue auditsnapshotaccrual(const UniValue& params, bool fHelp) const uint64_t height, const int64_t low_time, const int64_t high_time, - const int64_t claimed) + const int64_t claimed, + const Allocation magnitude_unit, + CAmount max_reward) { const GRC::Magnitude magnitude = superblock->m_cpids.MagnitudeOf(*cpid); @@ -393,7 +395,7 @@ UniValue auditsnapshotaccrual(const UniValue& params, bool fHelp) // correctly in the bignumber calculations. const uint64_t base_accrual = abs_time_interval * magnitude.Scaled() - * MAG_UNIT_NUMERATOR; + * magnitude_unit.GetNumerator(); int64_t period = 0; @@ -402,7 +404,7 @@ UniValue auditsnapshotaccrual(const UniValue& params, bool fHelp) accrual_bn *= COIN; accrual_bn /= 86400; accrual_bn /= Magnitude::SCALE_FACTOR; - accrual_bn /= MAG_UNIT_DENOMINATOR; + accrual_bn /= magnitude_unit.GetDenominator(); period = accrual_bn.GetLow64() * (int64_t) sign; } @@ -412,14 +414,11 @@ UniValue auditsnapshotaccrual(const UniValue& params, bool fHelp) * COIN / 86400 / Magnitude::SCALE_FACTOR - / MAG_UNIT_DENOMINATOR; + / magnitude_unit.GetDenominator(); } accrual += period; - // TODO: Change this to refer to MaxReward() from the snapshot computer. - int64_t max_reward = 16384 * COIN; - if (accrual > max_reward) { int64_t overage = accrual - max_reward; @@ -450,13 +449,21 @@ UniValue auditsnapshotaccrual(const UniValue& params, bool fHelp) }; for (; pindex; pindex = pindex->pnext) { + // We are only going to use the accrual computer here for the magnitude unit and max_reward. + AccrualComputer computer = GRC::Tally::GetSnapshotComputer(*cpid, pindex->GetBlockTime(), pindex); + + int64_t max_reward = computer->MaxReward(); + if (pindex->ResearchSubsidy() > 0 && pindex->GetMiningId() == *cpid) { + tally_accrual_period( "stake", pindex->nHeight, pindex_low->nTime, pindex->nTime, - pindex->ResearchSubsidy()); + pindex->ResearchSubsidy(), + computer->MagnitudeUnit(), + max_reward); accrual = 0; pindex_low = pindex; @@ -466,13 +473,16 @@ UniValue auditsnapshotaccrual(const UniValue& params, bool fHelp) for (const auto& mrc : pindex->m_mrc_researchers) { // mrc payments are on the block previous to the staked block (the head of the chain when the mrc // was submitted. + if (mrc->m_cpid == *cpid) { tally_accrual_period( "mrc payment", pindex->nHeight, pindex_low->nTime, pindex->pprev->nTime, - mrc->m_research_subsidy); + mrc->m_research_subsidy, + computer->MagnitudeUnit(), + max_reward); accrual = 0; pindex_low = pindex->pprev; @@ -487,7 +497,9 @@ UniValue auditsnapshotaccrual(const UniValue& params, bool fHelp) pindex->nHeight, pindex_low->nTime, pindex->nTime, - 0); + 0, + computer->MagnitudeUnit(), + max_reward); pindex_low = pindex; } @@ -497,8 +509,13 @@ UniValue auditsnapshotaccrual(const UniValue& params, bool fHelp) } } + // We are only going to use the accrual computer here for the magnitude unit and max_reward. + AccrualComputer computer = GRC::Tally::GetSnapshotComputer(*cpid, pindex_low->GetBlockTime(), pindex_low); + + int64_t max_reward = computer->MaxReward(); + // The final period is from the last event till "now". - int64_t period = tally_accrual_period("tip", 0, pindex_low->nTime, now, 0); + int64_t period = tally_accrual_period("tip", 0, pindex_low->nTime, now, 0, computer->MagnitudeUnit(), max_reward); result.pushKV("cpid", cpid->ToString()); result.pushKV("accrual_account_exists", accrual_account_exists);