Skip to content

Commit

Permalink
Rewrite GRC::GetConstantBlockReward
Browse files Browse the repository at this point in the history
This refactors GetConstantBlockReward to use the blockchain
consensus paramters, and also removes the lookback limitation
for block v13+ to correspond with the removal of the contract
lookback window for protocol entries.
  • Loading branch information
jamescowens committed Oct 20, 2024
1 parent 5bc0759 commit c6b0d35
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 78 deletions.
8 changes: 4 additions & 4 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ class CMainParams : public CChainParams {
consensus.BlockV13Height = std::numeric_limits<int>::max();
consensus.PollV3Height = 2671700;
consensus.ProjectV2Height = 2671700;
consensus.DefaultConstantBlockReward = 10;
consensus.DefaultConstantBlockReward = 10 * COIN;
consensus.ConstantBlockRewardFloor = 0;
consensus.ConstantBlockRewardCeiling = 100;
consensus.ConstantBlockRewardCeiling = 100 * COIN;
consensus.InitialMRCFeeFractionPostZeroInterval = Fraction(2, 5);
consensus.MRCZeroPaymentInterval = 14 * 24 * 60 * 60;
consensus.MaxMandatorySideStakeTotalAlloc = Fraction(1, 4);
Expand Down Expand Up @@ -185,9 +185,9 @@ class CTestNetParams : public CChainParams {
consensus.BlockV13Height = std::numeric_limits<int>::max();
consensus.PollV3Height = 1944820;
consensus.ProjectV2Height = 1944820;
consensus.DefaultConstantBlockReward = 10;
consensus.DefaultConstantBlockReward = 10 * COIN;
consensus.ConstantBlockRewardFloor = 0;
consensus.ConstantBlockRewardCeiling = 100;
consensus.ConstantBlockRewardCeiling = 100 * COIN;
consensus.InitialMRCFeeFractionPostZeroInterval = Fraction(2, 5);
consensus.MRCZeroPaymentInterval = 10 * 60;
consensus.MaxMandatorySideStakeTotalAlloc = Fraction(1, 4);
Expand Down
57 changes: 43 additions & 14 deletions src/gridcoin/staking/reward.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,54 @@ CAmount GetCoinYearReward(int64_t nTime)

CAmount GRC::GetConstantBlockReward(const CBlockIndex* index)
{
CAmount reward = 0;
CAmount MIN_CBR = 0;
CAmount MAX_CBR = 0;

ProtocolEntryOption reward_entry = GetProtocolRegistry().Try("blockreward1");

// The constant block reward is set to a default, voted on value, but this can
// be overridden using an admin message. This allows us to change the reward
// amount without having to release a mandatory with updated rules. In the case
// there is a breach or leaked admin keys the rewards are clamped to twice that
// of the default value.
const CAmount MIN_CBR = 0;
const CAmount MAX_CBR = DEFAULT_CBR * 2;
// there is a breach or leaked admin keys the rewards are clamped.

CAmount reward = DEFAULT_CBR;
AppCacheEntry oCBReward = GetProtocolRegistry().GetProtocolEntryByKeyLegacy("blockreward1");
// Note that the use of the IsV13Enabled test here on the block index does not consider the possibility of
// an administrative contract entry prior to v13 that has aged out from the lookback window which could affect consensus.
// However this possibility is actually covered because 5.4.8.0 was actually a mandatory due to a problem with
// message contracts, and this included the registry for protocol entries, eliminating the lookback window limitation.
// There has been no administrative entry to change the CBR in mainnet prior to 5.4.8.0, so in fact this is a moot issue.
if (IsV13Enabled(index->nHeight)) {
// The default and the clamp for block v13+ is controlled by blockchain consensus parameters.
reward = Params().GetConsensus().DefaultConstantBlockReward;
MIN_CBR = Params().GetConsensus().ConstantBlockRewardFloor;
MAX_CBR = Params().GetConsensus().ConstantBlockRewardCeiling;

if (reward_entry != nullptr) {
// There is no contract lookback limitation v13+.
if (!ParseInt64(reward_entry->m_value, &reward)) {
error("%s: Cannot parse constant block reward from protocol entry: %s",
__func__, reward_entry->m_value);
}
}
} else {
// This is the default and clamp pre block v13. Note that an administrative entry to change the CBR prior to block v13
// that is outside the pre v13 clamp rules could cause the CBR to change at the v13 height as the new clamp rules above
// would then apply on the existing protocol entry.
reward = 10 * COIN;
MIN_CBR = 0;
MAX_CBR = Params().GetConsensus().DefaultConstantBlockReward * 2;

//TODO: refactor the expire checking to subroutine
//Note: time constant is same as GetBeaconPublicKey
if ((index->nTime - oCBReward.timestamp) <= (60 * 24 * 30 * 6 * 60)) {
// This is a little slippery, because if we ever change CAmount from a int64_t, this will
// break. It is unlikely to ever change, however, and avoids an extra copy/implicit cast.
if (!ParseInt64(oCBReward.value, &reward)) {
error("%s: Cannot parse constant block reward from protocol entry: %s",
__func__, oCBReward.value);
if (reward_entry != nullptr) {
// The contract lookback window here for block version less than v13 is the same as the standard contract
// replay lookback.
if (index->nTime - reward_entry->m_timestamp <= Params().GetConsensus().StandardContractReplayLookback) {
// This is a little slippery, because if we ever change CAmount from a int64_t, this will
// break. It is unlikely to ever change, however, and avoids an extra copy/implicit cast.
if (!ParseInt64(reward_entry->m_value, &reward)) {
error("%s: Cannot parse constant block reward from protocol entry: %s",
__func__, reward_entry->m_value);
}
}
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ class MRC;
typedef std::optional<Claim> ClaimOption;
}

static const int64_t DEFAULT_CBR = 10 * COIN;

/** Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. */
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC

Expand Down
209 changes: 151 additions & 58 deletions src/test/gridcoin_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,53 +18,53 @@ extern bool fTestNet;
extern leveldb::DB *txdb;

namespace {
// Arbitrary random characters generated with Python UUID.
const std::string TEST_CPID("17c65330c0924259b2f93c31d25b03ac");

struct GridcoinTestsConfig
{
GridcoinTestsConfig()
{
}

~GridcoinTestsConfig()
{
}
};

void AddProtocolEntry(const uint32_t& payload_version, const std::string& key, const std::string& value,
const CBlockIndex index, const bool& reset_registry = false)
{
GRC::ProtocolRegistry& registry = GRC::GetProtocolRegistry();

// Make sure the registry is reset.
if (reset_registry) registry.Reset();

CTransaction dummy_tx;
dummy_tx.nTime = index.nTime;

GRC::Contract contract;

if (payload_version < 2) {
contract = GRC::MakeContract<GRC::ProtocolEntryPayload>(
uint32_t {2}, // Contract version (pre v13)
GRC::ContractAction::ADD,
key,
value);
} else {
contract = GRC::MakeContract<GRC::ProtocolEntryPayload>(
uint32_t {3}, // Contract version (post v13)
GRC::ContractAction::ADD,
payload_version, // Protocol payload version (post v13)
key,
value,
GRC::ProtocolEntryStatus::ACTIVE);
}

dummy_tx.vContracts.push_back(contract);

registry.Add({contract, dummy_tx, &index});
}
// Arbitrary random characters generated with Python UUID.
const std::string TEST_CPID("17c65330c0924259b2f93c31d25b03ac");

struct GridcoinTestsConfig
{
GridcoinTestsConfig()
{
}

~GridcoinTestsConfig()
{
}
};

void AddProtocolEntry(const uint32_t& payload_version, const std::string& key, const std::string& value,
const CBlockIndex index, const bool& reset_registry = false)
{
GRC::ProtocolRegistry& registry = GRC::GetProtocolRegistry();

// Make sure the registry is reset.
if (reset_registry) registry.Reset();

CTransaction dummy_tx;
dummy_tx.nTime = index.nTime;

GRC::Contract contract;

if (payload_version < 2) {
contract = GRC::MakeContract<GRC::ProtocolEntryPayload>(
uint32_t {2}, // Contract version (pre v13)
GRC::ContractAction::ADD,
key,
value);
} else {
contract = GRC::MakeContract<GRC::ProtocolEntryPayload>(
uint32_t {3}, // Contract version (post v13)
GRC::ContractAction::ADD,
payload_version, // Protocol payload version (post v13)
key,
value,
GRC::ProtocolEntryStatus::ACTIVE);
}

dummy_tx.vContracts.push_back(contract);

registry.Add({contract, dummy_tx, &index});
}

} // anonymous namespace

Expand Down Expand Up @@ -105,12 +105,21 @@ BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(gridcoin_cbr_tests)

BOOST_AUTO_TEST_CASE(gridcoin_DefaultCBRShouldBe10)
BOOST_AUTO_TEST_CASE(gridcoin_DefaultCBRShouldBe10PreV13)
{
CBlockIndex index;
index.nTime = 1538066417;

BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index), DEFAULT_CBR);
BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index), 10 * COIN);
}

BOOST_AUTO_TEST_CASE(gridcoin_DefaultCBRShouldBeDefaultConstantBlockRewardForV13)
{
CBlockIndex index;
index.nTime = 1538066417;
index.nHeight = Params().GetConsensus().BlockV13Height;

BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index), Params().GetConsensus().DefaultConstantBlockReward);
}

// Note that payload versions 1 and 2 alternate here. This is to test the constructors and
Expand Down Expand Up @@ -154,7 +163,7 @@ BOOST_AUTO_TEST_CASE(gridcoin_ConfigurableCBRShouldOverrideDefault)
CBlockIndex index_2;
index_2.nVersion = 13;
index_2.nTime = time + 1;
index_2.nHeight = 2;
index_2.nHeight = Params().GetConsensus().BlockV13Height;

// Protocol payload version 2

Expand All @@ -176,7 +185,7 @@ BOOST_AUTO_TEST_CASE(gridcoin_ConfigurableCBRShouldOverrideDefault)
BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index_2), cbr);
}

BOOST_AUTO_TEST_CASE(gridcoin_NegativeCBRShouldClampTo0)
BOOST_AUTO_TEST_CASE(gridcoin_NegativeCBRShouldClampTo0PreV13)
{
const int64_t time = 123456;
CBlockIndex index;
Expand All @@ -203,7 +212,34 @@ BOOST_AUTO_TEST_CASE(gridcoin_NegativeCBRShouldClampTo0)
BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index), 0);
}

BOOST_AUTO_TEST_CASE(gridcoin_ConfigurableCBRShouldClampTo2xDefault)
BOOST_AUTO_TEST_CASE(gridcoin_NegativeCBRShouldClampToConstantBlockRewardFloorForV13)
{
const int64_t time = 123456;
CBlockIndex index;
index.nTime = time + 2;
index.nHeight = Params().GetConsensus().BlockV13Height;

auto& registry = GRC::GetProtocolRegistry();

AddProtocolEntry(2, "blockreward1", ToString(-1 * COIN), index, false);

if (GRC::ProtocolEntryOption entry = registry.TryActive("blockreward1")) {
LogPrint(BCLog::LogFlags::CONTRACT, "INFO: %s: Protocol entry: m_key %s, m_value %s, m_hash %s, m_previous_hash %s, "
"m_timestamp %" PRId64 ", m_status string %s",
__func__,
entry->m_key,
entry->m_value,
entry->m_hash.ToString(),
entry->m_previous_hash.ToString(),
entry->m_timestamp,
entry->StatusToString()
);
}

BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index), Params().GetConsensus().ConstantBlockRewardFloor);
}

BOOST_AUTO_TEST_CASE(gridcoin_ConfigurableCBRShouldClampTo2xDefaultPreV13)
{
const int64_t time = 123456;
CBlockIndex index;
Expand All @@ -212,7 +248,34 @@ BOOST_AUTO_TEST_CASE(gridcoin_ConfigurableCBRShouldClampTo2xDefault)

auto& registry = GRC::GetProtocolRegistry();

AddProtocolEntry(2, "blockreward1", ToString(DEFAULT_CBR * 3), index, false);
AddProtocolEntry(2, "blockreward1", ToString(Params().GetConsensus().DefaultConstantBlockReward * 3), index, false);

if (GRC::ProtocolEntryOption entry = registry.TryActive("blockreward1")) {
LogPrint(BCLog::LogFlags::CONTRACT, "INFO: %s: Protocol entry: m_key %s, m_value %s, m_hash %s, m_previous_hash %s, "
"m_timestamp %" PRId64 ", m_status string %s",
__func__,
entry->m_key,
entry->m_value,
entry->m_hash.ToString(),
entry->m_previous_hash.ToString(),
entry->m_timestamp,
entry->StatusToString()
);
}

BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index), Params().GetConsensus().DefaultConstantBlockReward * 2);
}

BOOST_AUTO_TEST_CASE(gridcoin_ConfigurableCBRShouldClampToConstantBlockRewardCeilingForV13)
{
const int64_t time = 123456;
CBlockIndex index;
index.nTime = time + 3;
index.nHeight = Params().GetConsensus().BlockV13Height;

auto& registry = GRC::GetProtocolRegistry();

AddProtocolEntry(2, "blockreward1", ToString(Params().GetConsensus().ConstantBlockRewardCeiling * 2), index, false);

if (GRC::ProtocolEntryOption entry = registry.TryActive("blockreward1")) {
LogPrint(BCLog::LogFlags::CONTRACT, "INFO: %s: Protocol entry: m_key %s, m_value %s, m_hash %s, m_previous_hash %s, "
Expand All @@ -227,12 +290,10 @@ BOOST_AUTO_TEST_CASE(gridcoin_ConfigurableCBRShouldClampTo2xDefault)
);
}

BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index), DEFAULT_CBR * 2);
BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index), Params().GetConsensus().ConstantBlockRewardCeiling);
}

// TODO: when the 180 day lookback is removed, this should be removed as a test as it will
// be irrelevant and invalid.
BOOST_AUTO_TEST_CASE(gridcoin_ObsoleteConfigurableCBRShouldResortToDefault)
BOOST_AUTO_TEST_CASE(gridcoin_ObsoleteConfigurableCBRShouldResortToDefaultPreV13)
{
CBlockIndex index_check;
index_check.nTime = 1538066417;
Expand Down Expand Up @@ -260,7 +321,39 @@ BOOST_AUTO_TEST_CASE(gridcoin_ObsoleteConfigurableCBRShouldResortToDefault)
);
}

BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index_check), DEFAULT_CBR);
BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index_check), Params().GetConsensus().DefaultConstantBlockReward);
}

BOOST_AUTO_TEST_CASE(gridcoin_PriorObsoleteConfigurableCBRShouldNotResortToDefaultforV13)
{
CBlockIndex index_check;
index_check.nTime = 1538066417;
index_check.nHeight = Params().GetConsensus().BlockV13Height;
const int64_t max_message_age = 60 * 60 * 24 * 180;

CBlockIndex index_add;
index_add.nTime = index_check.nTime - max_message_age - 1;
index_add.nHeight = 5;

auto& registry = GRC::GetProtocolRegistry();

AddProtocolEntry(2, "blockreward1", ToString(3 * COIN), index_add, false);

if (GRC::ProtocolEntryOption entry = registry.TryActive("blockreward1")) {
LogPrint(BCLog::LogFlags::CONTRACT, "INFO: %s: Protocol entry: m_key %s, m_value %s, m_hash %s, m_previous_hash %s, "
"m_timestamp %" PRId64 ", m_status string %s",
__func__,
entry->m_key,
entry->m_value,
entry->m_hash.ToString(),
entry->m_previous_hash.ToString(),
entry->m_timestamp,
entry->StatusToString()
);
}

BOOST_CHECK_EQUAL(GRC::GetConstantBlockReward(&index_check), 3 * COIN);
}


BOOST_AUTO_TEST_SUITE_END()

0 comments on commit c6b0d35

Please sign in to comment.