Skip to content

Commit

Permalink
Add calculated minimum mrc fees to getmrcinfo
Browse files Browse the repository at this point in the history
Also expand functionality of getmrcinfo to allow reporting of all
CPIDs or specific CPID, defaulting to the current wallet's CPID.
  • Loading branch information
jamescowens committed Sep 19, 2022
1 parent bc5aff9 commit 99473f1
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 36 deletions.
57 changes: 54 additions & 3 deletions src/gridcoin/mrc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ CAmount MRC::ComputeMRCFee() const
if (!cpid) return fee;

const ResearchAccount& account = Tally::GetAccount(*cpid);
const int64_t last_reward_time = account.LastRewardTime();
int64_t last_reward_time = account.LastRewardTime();

// Get the block index of the head of the chain at the time the MRC was filled out.
auto block_index_element = mapBlockIndex.find(m_last_block_hash);
Expand All @@ -112,10 +112,61 @@ CAmount MRC::ComputeMRCFee() const

int64_t payment_time = prev_block_pindex->nTime;

// This is for ComputeMRCFee computations on historical MRC contracts, where actual payments have already occurred,
// in which case the last_reward_time gotten from the accrual system above will not be the right context. In this
// case we need to go through the block index starting at prev_block_index backwards and manually search for the
// last reward. Unfortunately this is very expensive, but it is really only used in the getmrcinfo rpc call.
if (payment_time <= last_reward_time) {
Beacon_ptr beacon = GetBeaconRegistry().TryActive(*cpid, payment_time);
if (!beacon) return fee;

CBlockIndex* last_payment_index = prev_block_pindex;
bool found_last_payment = false;

// Find the last payment, whether normal stake or MRC.
for (; last_payment_index; last_payment_index = last_payment_index->pprev) {

// Historical normal stake payment
if (last_payment_index->m_researcher && last_payment_index->m_researcher->m_cpid == *cpid) {
found_last_payment = true;
last_reward_time = last_payment_index->nTime;
break;
}

// Historical MRC payment
for (const auto& mrc_context : last_payment_index->m_mrc_researchers) {
if (mrc_context->m_cpid == *cpid) {
found_last_payment = true;
last_reward_time = last_payment_index->pprev->nTime;
break;
}
}

// Last normal or MRC payment found. We need this because we have to break out of two levels
// for historical MRC payments.
if (found_last_payment) break;
}

// We have gone through the whole chain looking for past payments and none have been found, so
// the last_reward_time is set to the beacon time for the beacon that predates the mrc time.
if (!found_last_payment) {
// If the beacon was renewed and the time stamp of this beacon is greater than
// the time of the last_payment_index, then walk the beacon chain back to the previous beacon.
while (beacon->m_timestamp > prev_block_pindex->nTime && beacon->Renewed()) {
beacon = GetBeaconRegistry().GetBeaconDB().find(beacon->m_prev_beacon_hash)->second;
}

last_reward_time = beacon->m_timestamp;
}
}

int64_t mrc_payment_interval = 0;

// If there is a last reward recorded in the accrual system, then the payment interval for the MRC request starts
// there and goes to the head of the chain for the MRC in the mempool. If not, we use the age of the beacon.
// If there is a last reward recorded in the accrual system, or, in the historical context, the history walk
// has determined a last payment or beacon root datetime, then the payment interval for the MRC request starts
// there and goes to the paytime time in context, which is for non-historical MRC's the head of the chain for
// the MRC in the mempool, and for historical MRC's the time of the prev_block_index. If the last_reward_time is
// zero, we use the age of the current beacon.
if (!last_reward_time) {
const BeaconOption beacon = GetBeaconRegistry().Try(*cpid);;

Expand Down
2 changes: 2 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3124,6 +3124,8 @@ GRC::MRCFees CBlock::GetMRCFees() const EXCLUSIVE_LOCKS_REQUIRED(cs_main)

GRC::MRC mrc = contract.CopyPayloadAs<GRC::MRC>();

mrc_fees.m_mrc_minimum_calc_fees += mrc.ComputeMRCFee();

mrc_total_fees += mrc.m_fee;
mrc_fees.m_mrc_foundation_fees += mrc.m_fee * foundation_fee_fraction.GetNumerator()
/ foundation_fee_fraction.GetDenominator();
Expand Down
5 changes: 3 additions & 2 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,9 @@ namespace GRC {
class MRCFees
{
public:
CAmount m_mrc_foundation_fees = 0; //!< mrc fees to the foundation
CAmount m_mrc_staker_fees = 0; //!< mrc fees to the staker
CAmount m_mrc_foundation_fees = 0; //!< mrc fees to the foundation
CAmount m_mrc_staker_fees = 0; //!< mrc fees to the staker
CAmount m_mrc_minimum_calc_fees = 0; //!< minimum calculated mrc fees for validation
};

//!
Expand Down
200 changes: 170 additions & 30 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,52 +462,84 @@ UniValue dumpcontracts(const UniValue& params, bool fHelp)

UniValue getmrcinfo(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() > 3)
if (fHelp || params.size() > 4)
throw runtime_error(
"getmrcinfo [detailed MRC info [low height [high height]]]\n"
"getmrcinfo [detailed MRC info [CPID [low height [high height]]]]\n"
"\n"
"[detailed MRC info]: optional boolean to output MRC details.\n"
" Defaults to false.\n"
"[CPID]: optional CPID. Defaults to current wallet CPID.\n"
" Use \"*\" for all CPIDs (network wide).\n"
" Note that block level mrc summary statistics are\n"
" specific to the scope specified with CPID.\n"
"[low height]: optional low height for scope.\n"
" Defaults to V12 block height.\n"
"[high height]: optional high height for scope.\n"
" Defaults to current block.\n"
);

bool output_mrc_details = false;
bool output_all_cpids = false;

if (params.size() > 0) {
output_mrc_details = params[0].get_bool();
}

GRC::MiningId mining_id;

LOCK(cs_main);

if (params.size() > 1) {
std::string cpid_string = params[1].get_str();

if (cpid_string == "*") {
output_all_cpids = true;
} else {
mining_id = GRC::MiningId::Parse(cpid_string);
}
} else {
mining_id = GRC::Researcher::Get()->Id();
}

if (!output_all_cpids && !mining_id.Valid()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid CPID.");
}

GRC::CpidOption cpid = mining_id.TryCpid();

if (!output_all_cpids && !cpid) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "No data for investor.");
}

// No MRC's below V12 block height.
int low_height = Params().GetConsensus().BlockV12Height;
int high_height = 0;

if (params.size() > 1) {
if (params.size() > 2) {
// If specified low height is lower than V12 height, set to V12 height.
low_height = std::max(params[1].get_int(), low_height);
low_height = std::max(params[2].get_int(), low_height);
}

if (params.size() > 2) {
if (params.size() > 3) {
// High height can't be lower than the low height.
high_height = std::max(low_height, params[2].get_int());
high_height = std::max(low_height, params[3].get_int());
}

UniValue report(UniValue::VOBJ);
UniValue block_output_array(UniValue::VARR);

uint64_t total_mrcs_paid = 0;
uint64_t total_mrcs_fee_boosted = 0;

CAmount mrc_total_research_rewards = 0;
CAmount mrc_total_foundation_fees = 0;
CAmount mrc_total_staker_fees = 0;
CAmount mrc_total_calculated_minimum_fees = 0;
CAmount mrc_total_fee_boost = 0;

CBlock block;
UniValue block_output(UniValue::VOBJ);

LOCK(cs_main);

// Set default high_height here if not specified above now that lock on cs_main is taken.
if (!high_height) {
high_height = pindexBest->nHeight;
Expand All @@ -524,70 +556,178 @@ UniValue getmrcinfo(const UniValue& params, bool fHelp)
CAmount mrc_research_rewards = 0;

for (const auto& mrc_context : blockindex->m_mrc_researchers) {
mrc_research_rewards += mrc_context->m_research_subsidy;
if (output_all_cpids || mrc_context->m_cpid == *cpid) {
mrc_research_rewards += mrc_context->m_research_subsidy;
}
}

if (!mrc_research_rewards) {
blockindex = blockindex->pnext;
continue;
}

ReadBlockFromDisk(block, blockindex, Params().GetConsensus());

// Get the claim which is where MRCs are actually paid.
GRC::Claim claim = block.GetClaim();
GRC::MRCFees mrc_fees = block.GetMRCFees();

uint64_t mrcs_paid = claim.m_mrc_tx_map.size(); // This also matches the size of the blockindex->m_mrc_researchers
GRC::MRCFees mrc_fees;
CAmount mrc_fee_boost = 0;
uint64_t mrcs_paid = 0;
uint64_t mrcs_fee_boosted = 0;

if (output_all_cpids) {
mrc_fees = block.GetMRCFees();
mrc_fee_boost = mrc_fees.m_mrc_foundation_fees
+ mrc_fees.m_mrc_staker_fees
- mrc_fees.m_mrc_minimum_calc_fees;

if (output_mrc_details) {
mrcs_paid = claim.m_mrc_tx_map.size(); // This also matches the size of the blockindex->m_mrc_researchers

if (output_mrc_details) {
UniValue mrc_requests_output_array(UniValue::VARR);
uint64_t mrc_requests = 0;

block_output.pushKV("hash", block.GetHash().GetHex());
block_output.pushKV("height", blockindex->nHeight);
block_output.pushKV("mrc_research_rewards", ValueFromAmount(mrc_research_rewards));
block_output.pushKV("mrc_foundation_fees", ValueFromAmount(mrc_fees.m_mrc_foundation_fees));
block_output.pushKV("mrc_staker_fees", ValueFromAmount(mrc_fees.m_mrc_staker_fees));
block_output.pushKV("mrc_net_paid_to_researchers", ValueFromAmount(mrc_research_rewards
- mrc_fees.m_mrc_foundation_fees
- mrc_fees.m_mrc_staker_fees));
block_output.pushKV("mrc_calculated_minimum_fees", ValueFromAmount(mrc_fees.m_mrc_minimum_calc_fees));
block_output.pushKV("mrc_fee_boost", ValueFromAmount(mrc_fee_boost));
block_output.pushKV("mrcs_paid", mrcs_paid);
block_output.pushKV("claim", ClaimToJson(block.GetClaim(), blockindex));

for (const auto& tx : block.vtx) {
for (const auto& contract : tx.GetContracts()) {
// We are only interested in MRC request contracts here.
if (contract.m_type != GRC::ContractType::MRC) continue;

GRC::MRC mrc = contract.CopyPayloadAs<GRC::MRC>();

++mrc_requests;

CAmount mrc_calculated_min_fee = mrc.ComputeMRCFee();

UniValue mrc_output(UniValue::VOBJ);

mrc_output.pushKV("txid", tx.GetHash().GetHex());
mrc_output.pushKVs(MRCToJson(mrc));
mrc_output.pushKV("mrc_calculated_minimum_fee", ValueFromAmount(mrc_calculated_min_fee));

if (mrc.m_fee > mrc_calculated_min_fee) ++mrcs_fee_boosted;

mrc_requests_output_array.push_back(mrc_output);

} // contracts
} // transaction

block_output.pushKV("mrc_requests", mrc_requests_output_array);

if (mrc_requests) {
block_output_array.push_back(block_output);
}
} else { // no details, but get the # of mrcs that had fee boosting
for (const auto& tx : block.vtx) {
for (const auto& contract : tx.GetContracts()) {
// We are only interested in MRC request contracts here.
if (contract.m_type != GRC::ContractType::MRC) continue;

GRC::MRC mrc = contract.CopyPayloadAs<GRC::MRC>();

CAmount mrc_calculated_min_fee = mrc.ComputeMRCFee();

if (mrc.m_fee > mrc_calculated_min_fee) ++mrcs_fee_boosted;
} // contracts
} // transaction
} // output_mrc_details
} else { // specific CPID
UniValue mrc_requests_output_array(UniValue::VARR);
uint64_t mrc_requests = 0;

block_output.pushKV("hash", block.GetHash().GetHex());
block_output.pushKV("height", blockindex->nHeight);
block_output.pushKV("mrc_research_rewards", ValueFromAmount(mrc_research_rewards));
block_output.pushKV("mrc_foundation_fees", ValueFromAmount(mrc_fees.m_mrc_foundation_fees));
block_output.pushKV("mrc_staker_fees", ValueFromAmount(mrc_fees.m_mrc_staker_fees));
block_output.pushKV("mrc_net_paid_to_researchers", ValueFromAmount(mrc_research_rewards
- mrc_fees.m_mrc_foundation_fees
- mrc_fees.m_mrc_staker_fees));
block_output.pushKV("mrcs_paid", mrcs_paid);
block_output.pushKV("claim", ClaimToJson(block.GetClaim(), blockindex));

for (const auto& tx : block.vtx) {
for (const auto& contract : tx.GetContracts()) {
// We are only interested in MRC request contracts here.
if (contract.m_type != GRC::ContractType::MRC) continue;

++mrc_requests;
GRC::MRC mrc = contract.CopyPayloadAs<GRC::MRC>();

if (mrc.m_mining_id != *cpid) continue;

++mrcs_paid;

Fraction foundation_fee_fraction = FoundationSideStakeAllocation();

CAmount mrc_foundation_fee = mrc.m_fee * foundation_fee_fraction.GetNumerator()
/ foundation_fee_fraction.GetDenominator();

mrc_fees.m_mrc_foundation_fees += mrc_foundation_fee;

mrc_fees.m_mrc_staker_fees += mrc.m_fee - mrc_foundation_fee;

CAmount mrc_calculated_min_fee = mrc.ComputeMRCFee();

mrc_fees.m_mrc_minimum_calc_fees += mrc_calculated_min_fee;
mrc_fee_boost += mrc.m_fee - mrc_calculated_min_fee;

UniValue mrc_output(UniValue::VOBJ);

mrc_output.pushKV("txid", tx.GetHash().GetHex());
mrc_output.pushKVs(MRCToJson(contract.CopyPayloadAs<GRC::MRC>()));
mrc_output.pushKVs(MRCToJson(mrc));
mrc_output.pushKV("mrc_calculated_minimum_fee", ValueFromAmount(mrc_calculated_min_fee));

if (mrc.m_fee > mrc_calculated_min_fee) ++mrcs_fee_boosted;

mrc_requests_output_array.push_back(mrc_output);

} // contracts
} // transaction

block_output.pushKV("mrc_requests", mrc_requests_output_array);

if (mrc_requests) {
block_output_array.push_back(block_output);
if (output_mrc_details) {
block_output.pushKV("hash", block.GetHash().GetHex());
block_output.pushKV("height", blockindex->nHeight);
block_output.pushKV("mrc_research_rewards", ValueFromAmount(mrc_research_rewards));
block_output.pushKV("mrc_foundation_fees", ValueFromAmount(mrc_fees.m_mrc_foundation_fees));
block_output.pushKV("mrc_staker_fees", ValueFromAmount(mrc_fees.m_mrc_staker_fees));
block_output.pushKV("mrc_net_paid_to_researchers", ValueFromAmount(mrc_research_rewards
- mrc_fees.m_mrc_foundation_fees
- mrc_fees.m_mrc_staker_fees));
block_output.pushKV("mrc_calculated_minimum_fees", ValueFromAmount(mrc_fees.m_mrc_minimum_calc_fees));
block_output.pushKV("mrc_fee_boost", ValueFromAmount(mrc_fee_boost));
block_output.pushKV("mrcs_paid", mrcs_paid);
block_output.pushKV("claim", ClaimToJson(block.GetClaim(), blockindex));

block_output.pushKV("mrc_requests", mrc_requests_output_array);

if (mrcs_paid) {
block_output_array.push_back(block_output);
}
}
}

mrc_total_foundation_fees += mrc_fees.m_mrc_foundation_fees;
mrc_total_staker_fees += mrc_fees.m_mrc_staker_fees;
mrc_total_calculated_minimum_fees += mrc_fees.m_mrc_minimum_calc_fees;
mrc_total_fee_boost += mrc_fee_boost;
total_mrcs_paid += mrcs_paid;
total_mrcs_fee_boosted += mrcs_fee_boosted;
mrc_total_research_rewards += mrc_research_rewards;
blockindex = blockindex->pnext;
} // while (pblockindex...)

report.pushKV("total_mrcs_paid", total_mrcs_paid);
report.pushKV("total_mrcs_fee_boosted", total_mrcs_fee_boosted);
report.pushKV("mrc_total_research_rewards", ValueFromAmount(mrc_total_research_rewards));
report.pushKV("mrc_total_foundation_fees", ValueFromAmount(mrc_total_foundation_fees));
report.pushKV("mrc_total_staker_fees", ValueFromAmount(mrc_total_staker_fees));
report.pushKV("mrc_total_net_paid_to_researchers", ValueFromAmount(mrc_total_research_rewards
- mrc_total_foundation_fees
- mrc_total_staker_fees));
report.pushKV("mrc_total_calculated_minimum_fees", ValueFromAmount(mrc_total_calculated_minimum_fees));
report.pushKV("mrc_total_fee_boost", ValueFromAmount(mrc_total_fee_boost));

if (output_mrc_details) {
report.pushKV("mrc_details_by_block", block_output_array);
}
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createmrcrequest" , 0 },
{ "createmrcrequest" , 1 },
{ "getmrcinfo" , 0 },
{ "getmrcinfo" , 1 },
{ "getmrcinfo" , 2 },
{ "getmrcinfo" , 3 },
{ "superblocks" , 0 },
{ "superblocks" , 1 },

Expand Down

0 comments on commit 99473f1

Please sign in to comment.