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

rpc, util, consensus: Implement exception handling framework for MRC and fix ValidateMRC to deal with testnet consensus issue #2508

Merged
merged 4 commits into from
May 7, 2022
Merged
Show file tree
Hide file tree
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
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