Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

consensus: Switch snapshot accrual calculation to integer arithmetic #1799

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 82 additions & 21 deletions src/neuralnet/accrual/snapshot.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "arith_uint256.h"
#include "fs.h"
#include "neuralnet/account.h"
#include "neuralnet/accrual/computer.h"
Expand All @@ -19,6 +20,18 @@ namespace {
using namespace NN;
using LogFlags = BCLog::LogFlags;

//!
//! \brief Numerator of the static magnitude unit coefficient for snapshot
//! accrual (block version 11 and greater).
//!
constexpr int64_t MAG_UNIT_NUMERATOR = 1;

//!
//! \brief Denominator of the static magnitude unit coefficient for snapshot
//! 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
//! accrued research rewards of the CPID's research account to rewards accrued
Expand All @@ -42,6 +55,12 @@ class SnapshotCalculator
//!
//! \brief Get the magnitude unit factored into the reward calculation.
//!
//! CONSENSUS: This method produces a semantic floating-point value for
//! the magnitude unit. Do not use this value directly to implement any
//! consensus-critial routine. Instead, prefer integer arithmetic for a
//! protocol implementation that needs to avoid floating-point error or
//! that requires portability between platforms.
//!
//! \return Amount paid per unit of magnitude per day in units of GRC.
//!
static double MagnitudeUnit()
Expand All @@ -65,9 +84,9 @@ class SnapshotCalculator
//
// daily_emission / total_magnitude = magnitude_unit = 0.23188405...
//
// ...rounded-up to:
// ...rounded-up to 0.25:
//
return 0.25;
return static_cast<double>(MAG_UNIT_NUMERATOR) / MAG_UNIT_DENOMINATOR;
}

//!
Expand All @@ -81,7 +100,7 @@ class SnapshotCalculator
//!
int64_t AccrualDelta(const Cpid& cpid, const ResearchAccount& account) const
{
double accrual_days;
int64_t accrual_timespan;

// If the CPID earned a reward on or after the current superblock, we
// calculate the reward using plain research age. The CPID carries no
Expand All @@ -94,12 +113,47 @@ class SnapshotCalculator
// CPIDs first appear in a superblock.
//
if (account.LastRewardHeight() >= m_superblock.m_height) {
accrual_days = AccrualDays(account);
accrual_timespan = AccrualAge(account);
} else {
accrual_days = SuperblockAgeDays();
accrual_timespan = SuperblockAge();
}

// CONSENSUS: avoid floating-point arithmetic.
//
// Some 32-bit x86 implementations do not perfectly conform to the IEEE
// 754 floating-point spec for extended precision. To sustain consensus
// on these platforms, we eschew the semantic representation of accrual
// calculation for block version 11+ and compute accrual using integer-
// only arithmetic. The following produces an accrual value defined by:
//
// accrual_days * CurrentMagnitude(cpid) * MagnitudeUnit() * COIN
//
// We deconstruct the magnitude unit coefficient ratio as a reminder to
// review this value if the protocol rule changes in the future:
//
const uint64_t base_accrual = accrual_timespan
* CurrentMagnitude(cpid).Scaled()
* MAG_UNIT_NUMERATOR;

// If the accrual calculation will overflow a 64-bit integer, we need
// more bits. Arithmetic with the big integer type is much slower and
// unnecessary in the majority of cases so switch only when required:
//
if (base_accrual > std::numeric_limits<uint64_t>::max() / COIN) {
arith_uint256 accrual_bn(base_accrual);
accrual_bn *= COIN;
accrual_bn /= 86400;
accrual_bn /= Magnitude::SCALE_FACTOR;
accrual_bn /= MAG_UNIT_DENOMINATOR;

return accrual_bn.GetLow64();
}

return accrual_days * CurrentMagnitude(cpid) * MagnitudeUnit() * COIN;
return base_accrual
* COIN
/ 86400
/ Magnitude::SCALE_FACTOR
/ MAG_UNIT_DENOMINATOR;
}

protected:
Expand All @@ -114,9 +168,9 @@ class SnapshotCalculator
//! \return Magnitude of the CPID in the superblock or zero if the CPID
//! does not exist in the superblock.
//!
double CurrentMagnitude(const Cpid& cpid) const
Magnitude CurrentMagnitude(const Cpid& cpid) const
{
return m_superblock->m_cpids.MagnitudeOf(cpid).Floating();
return m_superblock->m_cpids.MagnitudeOf(cpid);
}

//!
Expand All @@ -137,31 +191,21 @@ class SnapshotCalculator
return 0;
}

//!
//! \brief Get the number of days since the account's last research reward.
//!
//! \return Elapsed time in days.
//!
double AccrualDays(const ResearchAccount& account) const
{
return AccrualAge(account) / 86400.0;
}

//!
//! \brief Calculate the age of the active superblock to determine the
//! duration of the accrual period.
//!
//! \return Superblock age as days in until to the payment time.
//!
double SuperblockAgeDays() const
int64_t SuperblockAge() const
{
const int64_t timespan = m_payment_time - m_superblock.m_timestamp;

if (timespan <= 0) {
return 0;
}

return timespan / 86400.0;
return timespan;
}
}; // SnapshotCalculator

Expand Down Expand Up @@ -216,6 +260,17 @@ class SnapshotAccrualComputer : public IAccrualComputer, SnapshotCalculator
return 16384 * COIN;
}

//!
//! \brief Get the magnitude unit factored into the reward calculation.
//!
//! CONSENSUS: This method produces a semantic floating-point value for
//! the magnitude unit. Do not use this value directly to implement any
//! consensus-critial routine. Instead, prefer integer arithmetic for a
//! protocol implementation that needs to avoid floating-point error or
//! that requires portability between platforms.
//!
//! \return Amount paid per unit of magnitude per day in units of GRC.
//!
double MagnitudeUnit() const override
{
return SnapshotCalculator::MagnitudeUnit();
Expand Down Expand Up @@ -250,6 +305,9 @@ class SnapshotAccrualComputer : public IAccrualComputer, SnapshotCalculator

double AccrualDays() const override
{
// Since this informational value is not consensus-critical, we use
// floating-point arithmetic for readability:
//
return AccrualAge() / 86400.0;
}

Expand Down Expand Up @@ -294,7 +352,10 @@ class SnapshotAccrualComputer : public IAccrualComputer, SnapshotCalculator

int64_t ExpectedDaily() const override
{
return CurrentMagnitude(m_cpid) * MagnitudeUnit() * COIN;
// Since this informational value is not consensus-critical, we use
// floating-point arithmetic for readability:
//
return CurrentMagnitude(m_cpid).Floating() * MagnitudeUnit() * COIN;
}

int64_t RawAccrual() const override
Expand Down