Skip to content

Commit

Permalink
Switch snapshot accrual calculation to integer arithmetic
Browse files Browse the repository at this point in the history
  • Loading branch information
cyrossignol committed Jul 21, 2020
1 parent cca0088 commit c1ae5a7
Showing 1 changed file with 82 additions and 21 deletions.
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

0 comments on commit c1ae5a7

Please sign in to comment.