Skip to content

Commit

Permalink
Merge pull request #2508 from jamescowens/mrc
Browse files Browse the repository at this point in the history
rpc, util, consensus: Implement exception handling framework for MRC and fix ValidateMRC to deal with testnet consensus issue
  • Loading branch information
jamescowens authored May 7, 2022
2 parents c6925db + 1b0c91d commit ab4ebfe
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 30 deletions.
58 changes: 39 additions & 19 deletions src/gridcoin/mrc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,30 +279,43 @@ bool TrySignMRC(
}
} // anonymous namespace

bool GRC::CreateMRC(CBlockIndex* pindex,
void GRC::CreateMRC(CBlockIndex* pindex,
MRC& mrc,
CAmount &nReward,
CAmount &fee,
CWallet* pwallet) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
const GRC::ResearcherPtr researcher = GRC::Researcher::Get();

mrc.m_mining_id = researcher->Id();

if (researcher->Status() == GRC::ResearcherStatus::NO_BEACON) {
error("%s: CPID eligible but no active beacon key so MRC cannot be formed.", __func__);

return false;
bool err = true;

switch (researcher->Status())
{
case GRC::ResearcherStatus::ACTIVE:
// Not an error.
err = false;
break;
case GRC::ResearcherStatus::NO_BEACON:
throw MRC_error(strprintf("%s: CPID eligible but no active beacon key so MRC cannot be formed.", __func__));
case GRC::ResearcherStatus::INVESTOR:
throw MRC_error(strprintf("%s: MRC request cannot be sent while wallet is in investor mode.", __func__));
case GRC::ResearcherStatus::NO_PROJECTS:
// This is handled as no positive research reward pending below.
err = false;
break;
case GRC::ResearcherStatus::POOL:
throw MRC_error(strprintf("%s: MRC request cannot be sent while wallet is in pool mode.", __func__));
}
if (err) assert(false);

mrc.m_mining_id = researcher->Id();

if (const GRC::CpidOption cpid = mrc.m_mining_id.TryCpid()) {
mrc.m_research_subsidy = GRC::Tally::GetAccrual(*cpid, pindex->nTime, pindex);

// If no pending research subsidy value exists, bail.
if (mrc.m_research_subsidy <= 0) {
error("%s: No positive research reward pending at time of mrc.", __func__);

return false;
throw MRC_error(strprintf("%s: No positive research reward pending at time of mrc.", __func__));
} else {
nReward = mrc.m_research_subsidy;
mrc.m_magnitude = GRC::Quorum::GetMagnitude(*cpid).Floating();
Expand All @@ -326,16 +339,25 @@ bool GRC::CreateMRC(CBlockIndex* pindex,
// Set the output fee equal to computed (for help with error handling)
CAmount provided_fee = fee;
fee = computed_mrc_fee;
return error("%s: Invalid fee specified for mrc. The specified fee of %s is not bounded by the "
"minimum calculated fee of %s and the computed research reward of %s.",
__func__,
FormatMoney(provided_fee),
FormatMoney(computed_mrc_fee),
FormatMoney(mrc.m_research_subsidy));

if (provided_fee < computed_mrc_fee) {
throw MRC_error(strprintf("%s: Invalid fee specified for mrc. The specified fee of %s is less than "
"the minimum calculated fee of %s.",
__func__,
FormatMoney(provided_fee),
FormatMoney(computed_mrc_fee)));
} else {
throw MRC_error(strprintf("%s: Invalid fee specified for mrc. The specified fee of %s is greater than "
"the computed research reward of %s.",
__func__,
FormatMoney(provided_fee),
FormatMoney(mrc.m_research_subsidy)));

}
}

if (!TrySignMRC(pwallet, pindex, mrc)) {
return error("%s: Failed to sign mrc.", __func__);
throw MRC_error(strprintf("%s: Failed to sign mrc.", __func__));
}

LogPrintf(
Expand All @@ -345,7 +367,5 @@ bool GRC::CreateMRC(CBlockIndex* pindex,
FormatMoney(nReward),
mrc.m_magnitude,
FormatMoney(mrc.m_research_subsidy));

return true;
}

11 changes: 10 additions & 1 deletion src/gridcoin/mrc.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
class CPubKey;

namespace GRC {
class MRC_error : public std::runtime_error
{
public:
explicit MRC_error(const std::string& str) : std::runtime_error("ERROR: " + str)
{
LogPrintf("ERROR: %s", str);
}
};

//!
//! \brief Contains the reward claim context embedded in each generated block.
//!
Expand Down Expand Up @@ -341,7 +350,7 @@ class MRCContractHandler : public IContractHandler
//! \param pwallet: The wallet object
//! \return
//!
bool CreateMRC(CBlockIndex* pindex, MRC& mrc, CAmount &nReward, CAmount &fee, CWallet* pwallet);
void CreateMRC(CBlockIndex* pindex, MRC& mrc, CAmount &nReward, CAmount &fee, CWallet* pwallet);


} // namespace GRC
Expand Down
2 changes: 1 addition & 1 deletion src/gridcoin/staking/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ bool GRC::ReadStakedInput(
// Get transaction index for the previous transaction
if (!txdb.ReadTxIndex(prevout_hash, tx_index)) {
// Previous transaction not in main chain, may occur during initial download
return error("%s: tx index not found", __func__);
return error("%s: tx index not found for input tx %s", __func__, prevout_hash.GetHex());
}

const CDiskTxPos pos = tx_index.pos;
Expand Down
27 changes: 23 additions & 4 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4706,13 +4706,32 @@ bool ValidateMRC(const GRC::Contract& contract, const CTransaction& tx)
const GRC::ResearchAccount& account = GRC::Tally::GetAccount(*cpid);
const int64_t last_reward_time = account.LastRewardTime();

const int64_t payment_interval = mrc_time - last_reward_time;
const int64_t payment_interval_by_mrc = mrc_time - last_reward_time;
const int64_t payment_interval_by_tx_time = tx.nTime - last_reward_time;

if (payment_interval < reject_payment_interval) {
return error("%s: Validation failed: MRC payment interval by tx time, %" PRId64 " sec, is less than 1/2 of the MRC "
bool payment_interval_by_mrc_reject = (payment_interval_by_mrc < reject_payment_interval) ? true : false;
bool payment_interval_by_tx_time_reject = (payment_interval_by_tx_time < reject_payment_interval) ? true : false;;

if (!fTestNet && payment_interval_by_mrc_reject) {
return error("%s: Validation failed: MRC payment interval by mrc time, %" PRId64 " sec, is less than 1/2 of the MRC "
"Zero Payment Interval of %" PRId64 " sec.",
__func__,
payment_interval,
payment_interval_by_mrc,
reject_payment_interval);
}

// For testnet, both rejection conditions must be true (i.e. the payment interval by both mrc and tx time is less
// than 1/2 of MRCZeroPaymentInterval) for the transaction to be rejected. This difference from mainnet is to
// accomodate a post testnet v12 change in this function that originally shifted from tx time to mrc time for MRC
// payment interval rejection, after some mrc tests were already done post mandatory, which broke syncing from zero.
//
// TODO: On the next mandatory align the restriction to mainnet from that point forward.
if (fTestNet && payment_interval_by_mrc_reject && payment_interval_by_tx_time_reject) {
return error("%s: Validation failed: MRC payment interval on testnet by both mrc time, %" PRId64 " sec, "
"and tx time, %" PRId64 " is less than 1/2 of the MRC Zero Payment Interval of %" PRId64 " sec.",
__func__,
payment_interval_by_mrc,
payment_interval_by_tx_time,
reject_payment_interval);
}

Expand Down
6 changes: 4 additions & 2 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2530,8 +2530,10 @@ UniValue createmrcrequest(const UniValue& params, const bool fHelp) {
// If the fee is not overridden by the provided fee above (i.e. zero), it will be filled in
// at the calculated mrc value by CreateMRC. CreateMRC also rechecks the bounds
// of the provided fee.
if (!GRC::CreateMRC(pindex, mrc, reward, fee, pwalletMain)) {
throw runtime_error("MRC request creation failed. Please check the log for details.");
try {
GRC::CreateMRC(pindex, mrc, reward, fee, pwalletMain);
} catch (GRC::MRC_error& e) {
throw runtime_error(e.what());
}

if (!dry_run && !force && reward == fee) {
Expand Down
6 changes: 3 additions & 3 deletions src/test/gridcoin/mrc_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ BOOST_AUTO_TEST_CASE(createmrc_creates_valid_mrcs)
{
account.m_accrual = 72;
GRC::MRC mrc;
CAmount reward, fee{0};
CAmount reward{0}, fee{0};
GRC::CreateMRC(pindex->pprev, mrc, reward, fee, wallet);

BOOST_CHECK_EQUAL(reward, 72);
Expand All @@ -207,7 +207,7 @@ BOOST_AUTO_TEST_CASE(it_accepts_valid_fees)
{
account.m_accrual = 72;
GRC::MRC mrc;
CAmount reward, fee{0};
CAmount reward{0}, fee{0};
GRC::CreateMRC(pindex->pprev, mrc, reward, fee, wallet);

mrc.m_fee = 14;
Expand Down Expand Up @@ -239,7 +239,7 @@ BOOST_AUTO_TEST_CASE(it_creates_valid_mrc_claims)
BOOST_CHECK(CreateRestOfTheBlock(block, pindex->pprev, mrc_map));

GRC::MRC mrc;
CAmount reward, fee;
CAmount reward{0}, fee{0};
GRC::CreateMRC(pindex->pprev, mrc, reward, fee, wallet);
mrc_map[cpid] = {uint256{}, mrc};

Expand Down

0 comments on commit ab4ebfe

Please sign in to comment.