Skip to content

Commit

Permalink
Implement auto greylist boost unit test
Browse files Browse the repository at this point in the history
Made small adjustments as a result of unit testing. Note that
a tough to track down error was occurring due to an incorrect
action from the MarkAsSuperblock() call on the CBlockIndex object.
It turns out this was a enum confusion caused by SUPERBLOCK
being used in two different enums in the same namespace. This
has been corrected by puting the MinedType enum in the GRC
namespace.
  • Loading branch information
jamescowens committed Jan 23, 2025
1 parent 7960dfa commit b545355
Show file tree
Hide file tree
Showing 10 changed files with 874 additions and 100 deletions.
59 changes: 44 additions & 15 deletions src/gridcoin/project.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,9 @@ void AutoGreylist::Refresh() EXCLUSIVE_LOCKS_REQUIRED (cs_main)
RefreshWithSuperblock(superblock_ptr);
}

void AutoGreylist::RefreshWithSuperblock(SuperblockPtr superblock_ptr_in) EXCLUSIVE_LOCKS_REQUIRED (cs_main)
void AutoGreylist::RefreshWithSuperblock(SuperblockPtr superblock_ptr_in,
std::shared_ptr<std::map<int, std::pair<CBlockIndex*, SuperblockPtr>>> unit_test_blocks)
EXCLUSIVE_LOCKS_REQUIRED (cs_main)
{
if (superblock_ptr_in.IsEmpty()) {
return;
Expand All @@ -419,7 +421,7 @@ void AutoGreylist::RefreshWithSuperblock(SuperblockPtr superblock_ptr_in) EXCLUS

LOCK(autogreylist_lock);

m_greylist_ptr->clear();
m_greylist_ptr->clear();

// No need to go further if the whitelist is empty (ignoring deleted records).
if (!whitelist.Populated()) {
Expand Down Expand Up @@ -452,13 +454,13 @@ void AutoGreylist::RefreshWithSuperblock(SuperblockPtr superblock_ptr_in) EXCLUS
if (project != superblock_ptr_in->m_projects_all_cpids_total_credits.m_projects_all_cpid_total_credits.end()) {
// Record new greylist candidate entry baseline with the total credit for each project present in superblock.
m_greylist_ptr->insert(std::make_pair(iter.m_name,
GreylistCandidateEntry(iter.m_name, std::nearbyint(project->second))));
GreylistCandidateEntry(iter.m_name, project->second)));
} else {
// Record new greylist candidate entry with nullopt total credit. This is for a project that is in the whitelist,
// but does not have a project entry in the superblock. This would be because the scrapers could not converge on the
// project.
m_greylist_ptr->insert(std::make_pair(iter.m_name, GreylistCandidateEntry(iter.m_name,
std::optional<uint64_t>(std::nullopt))));
std::optional<uint64_t>())));
}
}
}
Expand All @@ -467,8 +469,24 @@ void AutoGreylist::RefreshWithSuperblock(SuperblockPtr superblock_ptr_in) EXCLUS

CBlockIndex* index_ptr;
{
// Find the block index entry for the block before the provided superblock_ptr.
index_ptr = GRC::BlockFinder::FindByHeight(superblock_ptr_in.m_height - 1);
// Find the block index entry for the block before the provided superblock_ptr. This also implements the unit test
// substitute input structure.
if (unit_test_blocks == nullptr) {
index_ptr = GRC::BlockFinder::FindByHeight(superblock_ptr_in.m_height - 1);
} else {
// This only works if the unit_test_blocks are all superblocks, which they should be based on the setup of the
// unit test.
auto iter = unit_test_blocks->find(superblock_ptr_in.m_height - 1);

if (iter != unit_test_blocks->end()) {
// Get the unit test entry that corresponds to the superblock_ptr_in, which was processed above, and then
// get CBlockIndex* entry.
index_ptr = iter->second.first;
} else {
index_ptr = nullptr;
}

}
}


Expand All @@ -482,16 +500,27 @@ void AutoGreylist::RefreshWithSuperblock(SuperblockPtr superblock_ptr_in) EXCLUS
// For some reason this is not working.
//superblock_ptr.ReadFromDisk(index_ptr);

CBlock block;
if (!ReadBlockFromDisk(block, index_ptr, Params().GetConsensus())) {
error("%s: Failed to read block from disk with requested height %u",
__func__,
index_ptr->nHeight);
continue;
}
SuperblockPtr superblock_ptr;

SuperblockPtr superblock_ptr = block.GetClaim().m_superblock;
superblock_ptr.Rebind(index_ptr);
if (unit_test_blocks == nullptr) {
CBlock block;

if (!ReadBlockFromDisk(block, index_ptr, Params().GetConsensus())) {
error("%s: Failed to read block from disk with requested height %u",
__func__,
index_ptr->nHeight);
continue;
}

superblock_ptr = block.GetClaim().m_superblock;
superblock_ptr.Rebind(index_ptr);
} else {
auto iter = unit_test_blocks->find(index_ptr->nHeight);

if (iter != unit_test_blocks->end()) {
superblock_ptr = iter->second.second;
}
}

// Stop if superblocks less than version 3 are encountered while going backwards. This will happen until we are 40
// superblocks past the 1st superblock after the height specified for the changeover to v3 superblocks.
Expand Down
32 changes: 20 additions & 12 deletions src/gridcoin/project.h
Original file line number Diff line number Diff line change
Expand Up @@ -567,9 +567,9 @@ class AutoGreylist
// Populate the initial historical entry from the initial baseline.
UpdateHistoryEntry entry = UpdateHistoryEntry(0,
TC_initial_bookmark,
std::optional<uint8_t> {},
std::optional<Fraction> {},
std::optional<bool> {});
std::optional<uint8_t>(),
std::optional<Fraction>(),
std::optional<bool>());

m_update_history.push_back(entry);
}
Expand Down Expand Up @@ -615,8 +615,8 @@ class AutoGreylist
// 64 bit TC averages will overflow a signed 64 integer, and if so, then simply divide by 2 before constructing the
// fraction. This is extremely unlikely, given that the number of SB's processed from the baseline would have to be
// one, and then the sum overflow the int64_t max.
uint64_t TC_7_SB_avg = m_TC_7_SB_sum / std::min<uint64_t>(m_sb_from_baseline_processed, 7);
uint64_t TC_40_SB_avg = m_TC_40_SB_sum / std::min<uint64_t>(m_sb_from_baseline_processed, 40);
uint64_t TC_7_SB_avg = m_TC_7_SB_sum / std::min<uint64_t>(m_sb_from_baseline_processed, 6);
uint64_t TC_40_SB_avg = m_TC_40_SB_sum / std::min<uint64_t>(m_sb_from_baseline_processed, 39);

if (TC_7_SB_avg > (uint64_t) std::numeric_limits<int64_t>::max()
|| TC_40_SB_avg > (uint64_t) std::numeric_limits<int64_t>::max()) {
Expand All @@ -642,8 +642,8 @@ class AutoGreylist
if (sb_from_baseline > 0) {
// ZCD part. Remember we are going backwards, so if total_credit is greater than or equal to
// the bookmark, then we have zero or even negative project total credit between superblocks, so
// this qualifies as a ZCD.
if (sb_from_baseline <= 20) {
// this qualifies as a ZCD. We look back up to 19 from baseline (20 SBs total).
if (sb_from_baseline < 20) {
// If total credit is greater than the bookmark, this means that (going forward in time) credit actually
// declined, so we must add a day for the credit decline (going forward in time). If total credit
// is std::nullopt, then this means no statistics available, which also is a ZCD.
Expand All @@ -652,7 +652,7 @@ class AutoGreylist
}
}

// WAS part. Here we deal with two numbers, the 40 SB from baseline, and the 7 SB from baseline. We use
// WAS part. Here we deal with two numbers, the 40 SB (39 from baseline), and the 7 SB (6 from baseline). We use
// the initial bookmark, and compute the difference, which is the same as adding up the deltas between
// the TC's in each superblock. For updates with no total credit entry (i.e. std::optional is nullopt),
// do not change the sums.
Expand All @@ -664,11 +664,11 @@ class AutoGreylist
}

if (total_credit && m_TC_initial_bookmark > total_credit) {
if (sb_from_baseline <= 7) {
if (sb_from_baseline < 7) {
m_TC_7_SB_sum = *m_TC_initial_bookmark - *total_credit;
}

if (sb_from_baseline <= 40) {
if (sb_from_baseline < 40) {
m_TC_40_SB_sum = *m_TC_initial_bookmark - *total_credit;
}
}
Expand All @@ -685,7 +685,7 @@ class AutoGreylist
Fraction was = GetWAS();

// Apply rules and determine if greylisting criteria is met.
m_meets_greylisting_crit = (sb_from_baseline >= 2 && (zcd > 7 || was < Fraction(1, 10)));
m_meets_greylisting_crit = (sb_from_baseline >= 1 && (zcd > 7 || was < Fraction(1, 10)));

// Insert historical entry.
UpdateHistoryEntry entry(sb_from_baseline, total_credit, zcd, was, m_meets_greylisting_crit);
Expand All @@ -702,6 +702,8 @@ class AutoGreylist
//!
//! \param sb_from_baseline_processed
//! \param total_credit
//! \param zcd
//! \param was
//! \param meets_greylisting_crit
//!
UpdateHistoryEntry(uint8_t sb_from_baseline_processed,
Expand Down Expand Up @@ -806,7 +808,13 @@ class AutoGreylist
//! from a scraper convergence, or in the instance of this being called from Refresh(), could be the current
//! superblock on the chain.
//!
void RefreshWithSuperblock(SuperblockPtr superblock_ptr_in);
//! \param unit_test_blocks This is a map that is indexed by height, with linked CBlockIndex* pointers and paired
//! Superblock objects. This is intended as a substitute input structure for unit testing. It is optional and defaults to
//! nullptr, in which case the live chain data is used. One of the entries in the map must correspond to the Superblock_ptr
//! passed as the first parameter.
//!
void RefreshWithSuperblock(SuperblockPtr superblock_ptr_in,
std::shared_ptr<std::map<int, std::pair<CBlockIndex*, SuperblockPtr>>> unit_test_blocks = nullptr);

//!
//! \brief This refreshes the AutoGreylist object from an input Superblock that is going to be associated
Expand Down
22 changes: 11 additions & 11 deletions src/qt/transactiondesc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,38 +114,38 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, unsigned int vo
// Update support for Side Stake and correctly show POS/POR as well
strHTML += "<b>" + tr("Source") + ":</b> ";

MinedType gentype = GetGeneratedType(wallet, wtx.GetHash(), vout);
GRC::MinedType gentype = GetGeneratedType(wallet, wtx.GetHash(), vout);

switch (gentype)
{
case MinedType::POS:
case GRC::MinedType::POS:
strHTML += tr("Mined - PoS");
break;
case MinedType::POR:
case GRC::MinedType::POR:
strHTML += tr("Mined - PoS+RR");
break;
case MinedType::ORPHANED:
case GRC::MinedType::ORPHANED:
strHTML += tr("Mined - Orphaned");
break;
case MinedType::POS_SIDE_STAKE_RCV:
case GRC::MinedType::POS_SIDE_STAKE_RCV:
strHTML += tr("PoS Side Stake Received");
break;
case MinedType::POR_SIDE_STAKE_RCV:
case GRC::MinedType::POR_SIDE_STAKE_RCV:
strHTML += tr("PoS+RR Side Stake Received");
break;
case MinedType::POS_SIDE_STAKE_SEND:
case GRC::MinedType::POS_SIDE_STAKE_SEND:
strHTML += tr("PoS Side Stake Sent");
break;
case MinedType::POR_SIDE_STAKE_SEND:
case GRC::MinedType::POR_SIDE_STAKE_SEND:
strHTML += tr("PoS+RR Side Stake Sent");
break;
case MinedType::MRC_RCV:
case GRC::MinedType::MRC_RCV:
strHTML += tr("MRC Payment Received");
break;
case MinedType::MRC_SEND:
case GRC::MinedType::MRC_SEND:
strHTML += tr("MRC Payment Sent");
break;
case MinedType::SUPERBLOCK:
case GRC::MinedType::SUPERBLOCK:
strHTML += tr("Mined - Superblock");
break;
default:
Expand Down
4 changes: 2 additions & 2 deletions src/qt/transactionrecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class TransactionStatus
, sortKey("")
, matures_in(0)
, status(Offline)
, generated_type(MinedType::UNKNOWN)
, generated_type(GRC::MinedType::UNKNOWN)
, depth(0)
, open_for(0)
, cur_num_blocks(-1)
Expand Down Expand Up @@ -54,7 +54,7 @@ class TransactionStatus
/** @name Reported status
@{*/
Status status;
MinedType generated_type;
GRC::MinedType generated_type;
int64_t depth;
int64_t open_for; /**< Timestamp if status==OpenUntilDate, otherwise number
of additional blocks that need to be mined before
Expand Down
40 changes: 20 additions & 20 deletions src/qt/transactiontablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,25 +406,25 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const
{
switch (wtx->status.generated_type)
{
case MinedType::POS:
case GRC::MinedType::POS:
return tr("Mined - PoS");
case MinedType::POR:
case GRC::MinedType::POR:
return tr("Mined - PoS+RR");
case MinedType::ORPHANED:
case GRC::MinedType::ORPHANED:
return tr("Mined - Orphaned");
case MinedType::POS_SIDE_STAKE_RCV:
case GRC::MinedType::POS_SIDE_STAKE_RCV:
return tr("PoS Side Stake Received");
case MinedType::POR_SIDE_STAKE_RCV:
case GRC::MinedType::POR_SIDE_STAKE_RCV:
return tr("PoS+RR Side Stake Received");
case MinedType::POS_SIDE_STAKE_SEND:
case GRC::MinedType::POS_SIDE_STAKE_SEND:
return tr("PoS Side Stake Sent");
case MinedType::POR_SIDE_STAKE_SEND:
case GRC::MinedType::POR_SIDE_STAKE_SEND:
return tr("PoS+RR Side Stake Sent");
case MinedType::MRC_RCV:
case GRC::MinedType::MRC_RCV:
return tr("MRC Payment Received");
case MinedType::MRC_SEND:
case GRC::MinedType::MRC_SEND:
return tr("MRC Payment Sent");
case MinedType::SUPERBLOCK:
case GRC::MinedType::SUPERBLOCK:
return tr("Mined - Superblock");
default:
return tr("Mined - Unknown");
Expand Down Expand Up @@ -454,25 +454,25 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx
{
switch (wtx->status.generated_type)
{
case MinedType::POS:
case GRC::MinedType::POS:
return QIcon(":/icons/tx_pos");
case MinedType::POR:
case GRC::MinedType::POR:
return QIcon(":/icons/tx_por");
case MinedType::ORPHANED:
case GRC::MinedType::ORPHANED:
return QIcon(":/icons/transaction_conflicted");
case MinedType::POS_SIDE_STAKE_RCV:
case GRC::MinedType::POS_SIDE_STAKE_RCV:
return QIcon(":/icons/tx_pos_ss");
case MinedType::POR_SIDE_STAKE_RCV:
case GRC::MinedType::POR_SIDE_STAKE_RCV:
return QIcon(":/icons/tx_por_ss");
case MinedType::POS_SIDE_STAKE_SEND:
case GRC::MinedType::POS_SIDE_STAKE_SEND:
return QIcon(":/icons/tx_pos_ss_sent");
case MinedType::POR_SIDE_STAKE_SEND:
case GRC::MinedType::POR_SIDE_STAKE_SEND:
return QIcon(":/icons/tx_por_ss_sent");
case MinedType::MRC_RCV:
case GRC::MinedType::MRC_RCV:
return QIcon(":/icons/tx_por_ss");
case MinedType::MRC_SEND:
case GRC::MinedType::MRC_SEND:
return QIcon(":/icons/tx_por_ss_sent");
case MinedType::SUPERBLOCK:
case GRC::MinedType::SUPERBLOCK:
return QIcon(":/icons/superblock");
default:
return QIcon(":/icons/transaction_0");
Expand Down
Loading

0 comments on commit b545355

Please sign in to comment.