Skip to content

Commit

Permalink
Merge pull request #2575 from jamescowens/implement_mrc_min_calc_fees…
Browse files Browse the repository at this point in the history
…_display

rpc: getmrcinfo part 2 - add calculated minimum fees and fee boosting and by CPID reporting
  • Loading branch information
jamescowens committed Sep 21, 2022
2 parents bc5aff9 + 99473f1 commit f29a778
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 f29a778

Please sign in to comment.